From: Nikolay Sivov Subject: [PATCH] shlwapi: Implement PathUnExpandEnvStrings Message-Id: <1383307554.10516.0.camel@laptop> Date: Fri, 01 Nov 2013 16:05:54 +0400 SmartFTP uses that --- dlls/shlwapi/path.c | 117 ++++++++++++++++++++++++++++++++++++++++++---- dlls/shlwapi/tests/path.c | 80 ++++++++++++++++++++++++++++++- 2 files changed, 187 insertions(+), 10 deletions(-) diff --git a/dlls/shlwapi/path.c b/dlls/shlwapi/path.c index 415ae29..5f70bcc 100644 --- a/dlls/shlwapi/path.c +++ b/dlls/shlwapi/path.c @@ -58,6 +58,21 @@ static fnpIsNetDrive pIsNetDrive; HRESULT WINAPI SHGetWebFolderFilePathW(LPCWSTR,LPWSTR,DWORD); +static inline WCHAR* heap_strdupAtoW(LPCSTR str) +{ + WCHAR *ret = NULL; + + if (str) + { + DWORD len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0); + ret = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR)); + if (ret) + MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len); + } + + return ret; +} + /************************************************************************* * PathAppendA [SHLWAPI.@] * @@ -4040,18 +4055,61 @@ VOID WINAPI PathUndecorateW(LPWSTR lpszPath) * strings. * * PARAMS - * pszPath [I] Buffer containing the path to unexpand. - * pszBuf [O] Buffer to receive the unexpanded path. - * cchBuf [I] Size of pszBuf in characters. + * path [I] Buffer containing the path to unexpand. + * buffer [O] Buffer to receive the unexpanded path. + * buf_len [I] Size of pszBuf in characters. * * RETURNS * Success: TRUE * Failure: FALSE */ -BOOL WINAPI PathUnExpandEnvStringsA(LPCSTR pszPath, LPSTR pszBuf, UINT cchBuf) +BOOL WINAPI PathUnExpandEnvStringsA(LPCSTR path, LPSTR buffer, UINT buf_len) { - FIXME("(%s,%s,0x%08x)\n", debugstr_a(pszPath), debugstr_a(pszBuf), cchBuf); - return FALSE; + WCHAR bufferW[MAX_PATH], *pathW; + DWORD len; + BOOL ret; + + TRACE("(%s, %p, %d)\n", debugstr_a(path), buffer, buf_len); + + pathW = heap_strdupAtoW(path); + if (!pathW) return FALSE; + + ret = PathUnExpandEnvStringsW(pathW, bufferW, MAX_PATH); + HeapFree(GetProcessHeap(), 0, pathW); + if (!ret) return FALSE; + + len = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL); + if (buf_len < len + 1) return FALSE; + + WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, buf_len, NULL, NULL); + return TRUE; +} + +static const WCHAR allusersprofileW[] = {'%','A','L','L','U','S','E','R','S','P','R','O','F','I','L','E','%',0}; +static const WCHAR appdataW[] = {'%','A','P','P','D','A','T','A','%',0}; +static const WCHAR computernameW[] = {'%','C','O','M','P','U','T','E','R','N','A','M','E','%',0}; +static const WCHAR programfilesW[] = {'%','P','r','o','g','r','a','m','F','i','l','e','s','%',0}; +static const WCHAR systemrootW[] = {'%','S','y','s','t','e','m','R','o','o','t','%',0}; +static const WCHAR systemdriveW[] = {'%','S','y','s','t','e','m','D','r','i','v','e','%',0}; +static const WCHAR userprofileW[] = {'%','U','S','E','R','P','R','O','F','I','L','E','%',0}; + +struct envvars_map +{ + const WCHAR *var; + UINT varlen; + WCHAR path[MAX_PATH]; + DWORD len; +}; + +static void init_envvars_map(struct envvars_map *map) +{ + while (map->var) + { + map->len = ExpandEnvironmentStringsW(map->var, map->path, sizeof(map->path)/sizeof(WCHAR)); + /* exclude null from length */ + if (map->len) map->len--; + map++; + } } /************************************************************************* @@ -4059,10 +4117,51 @@ BOOL WINAPI PathUnExpandEnvStringsA(LPCSTR pszPath, LPSTR pszBuf, UINT cchBuf) * * Unicode version of PathUnExpandEnvStringsA. */ -BOOL WINAPI PathUnExpandEnvStringsW(LPCWSTR pszPath, LPWSTR pszBuf, UINT cchBuf) +BOOL WINAPI PathUnExpandEnvStringsW(LPCWSTR path, LPWSTR buffer, UINT buf_len) { - FIXME("(%s,%s,0x%08x)\n", debugstr_w(pszPath), debugstr_w(pszBuf), cchBuf); - return FALSE; + static struct envvars_map null_var = {NULL, 0, {0}, 0}; + struct envvars_map *match = &null_var, *cur; + struct envvars_map envvars[] = { + { allusersprofileW, sizeof(allusersprofileW)/sizeof(WCHAR) }, + { appdataW, sizeof(appdataW)/sizeof(WCHAR) }, + { computernameW, sizeof(computernameW)/sizeof(WCHAR) }, + { programfilesW, sizeof(programfilesW)/sizeof(WCHAR) }, + { systemrootW, sizeof(systemrootW)/sizeof(WCHAR) }, + { systemdriveW, sizeof(systemdriveW)/sizeof(WCHAR) }, + { userprofileW, sizeof(userprofileW)/sizeof(WCHAR) }, + { NULL } + }; + DWORD pathlen; + UINT needed; + + TRACE("(%s, %p, %d)\n", debugstr_w(path), buffer, buf_len); + + pathlen = strlenW(path); + init_envvars_map(envvars); + cur = envvars; + while (cur->var) + { + /* path can't contain expanded value or value wasn't retrieved */ + if (cur->len == 0 || cur->len > pathlen || strncmpiW(cur->path, path, cur->len)) + { + cur++; + continue; + } + + if (cur->len > match->len) + match = cur; + cur++; + } + + /* 'varlen' includes NULL termination char */ + needed = match->varlen + pathlen - match->len; + if (match->len == 0 || needed > buf_len) return FALSE; + + strcpyW(buffer, match->var); + strcatW(buffer, &path[match->len]); + TRACE("ret %s\n", debugstr_w(buffer)); + + return TRUE; } /************************************************************************* diff --git a/dlls/shlwapi/tests/path.c b/dlls/shlwapi/tests/path.c index 1cb6168..9e2e92d 100644 --- a/dlls/shlwapi/tests/path.c +++ b/dlls/shlwapi/tests/path.c @@ -34,6 +34,8 @@ static HRESULT (WINAPI *pPathCreateFromUrlA)(LPCSTR, LPSTR, LPDWORD, DWORD); static HRESULT (WINAPI *pPathCreateFromUrlW)(LPCWSTR, LPWSTR, LPDWORD, DWORD); static HRESULT (WINAPI *pPathCreateFromUrlAlloc)(LPCWSTR, LPWSTR*, DWORD); static BOOL (WINAPI *pPathAppendA)(LPSTR, LPCSTR); +static BOOL (WINAPI *pPathUnExpandEnvStringsA)(LPCSTR, LPSTR, UINT); +static BOOL (WINAPI *pPathUnExpandEnvStringsW)(LPCWSTR, LPWSTR, UINT); /* ################ */ @@ -1452,7 +1454,80 @@ static void test_PathGetDriveNumber(void) ok(ret == -1, "got %d\n", ret); } -/* ################ */ +static void test_PathUnExpandEnvStrings(void) +{ + static const WCHAR sysrootW[] = {'%','S','y','s','t','e','m','R','o','o','t','%',0}; + static const WCHAR nonpathW[] = {'p','a','t','h',0}; + static const char sysrootA[] = "%SystemRoot%"; + WCHAR pathW[MAX_PATH], buffW[MAX_PATH]; + char path[MAX_PATH], buff[MAX_PATH]; + BOOL ret; + UINT len; + + if (!pPathUnExpandEnvStringsA || !pPathUnExpandEnvStringsW) + { + win_skip("PathUnExpandEnvStrings not available\n"); + return; + } + + /* something that can't be represented with env var */ + strcpy(path, "somepath_name"); + strcpy(buff, "xx"); + ret = pPathUnExpandEnvStringsA(path, buff, sizeof(buff)); + ok(!ret, "got %d\n", ret); + ok(buff[0] == 'x', "wrong return string %s\n", buff); + + len = GetSystemDirectoryA(path, MAX_PATH); + ok(len > 0, "failed to get sysdir\n"); + + /* buffer size is not enough */ + strcpy(buff, "xx"); + ret = pPathUnExpandEnvStringsA(path, buff, 5); + ok(!ret, "got %d\n", ret); + ok(buff[0] == 'x', "wrong return string %s\n", buff); + + /* buffer size is enough to hold variable name only */ + strcpy(buff, "xx"); + ret = pPathUnExpandEnvStringsA(path, buff, sizeof(sysrootA)); + ok(!ret, "got %d\n", ret); + ok(buff[0] == 'x', "wrong return string %s\n", buff); + + /* enough size */ + buff[0] = 0; + ret = pPathUnExpandEnvStringsA(path, buff, sizeof(buff)); + ok(ret, "got %d\n", ret); + ok(!strncmp(buff, sysrootA, sizeof(sysrootA)-1), "wrong return string %s\n", buff); + + /* PathUnExpandEnvStringsW */ + + /* something that can't be represented with env var */ + lstrcpyW(pathW, nonpathW); + buffW[0] = 'x'; buffW[1] = 0; + ret = pPathUnExpandEnvStringsW(pathW, buffW, sizeof(buffW)/sizeof(WCHAR)); + ok(!ret, "got %d\n", ret); + ok(buffW[0] == 'x', "wrong return string %s\n", wine_dbgstr_w(buffW)); + + len = GetSystemDirectoryW(pathW, MAX_PATH); + ok(len > 0, "failed to get sysdir\n"); + + /* buffer size is not enough */ + buffW[0] = 'x'; buffW[1] = 0; + ret = pPathUnExpandEnvStringsW(pathW, buffW, 5); + ok(!ret, "got %d\n", ret); + ok(buffW[0] == 'x', "wrong return string %s\n", wine_dbgstr_w(buffW)); + + /* buffer size is enough to hold variable name only */ + buffW[0] = 'x'; buffW[1] = 0; + ret = pPathUnExpandEnvStringsW(pathW, buffW, sizeof(sysrootW)/sizeof(WCHAR)); + ok(!ret, "got %d\n", ret); + ok(buffW[0] == 'x', "wrong return string %s\n", wine_dbgstr_w(buffW)); + + /* enough size */ + buffW[0] = 0; + ret = pPathUnExpandEnvStringsW(pathW, buffW, sizeof(buffW)/sizeof(WCHAR)); + ok(ret, "got %d\n", ret); + ok(!memcmp(buffW, sysrootW, sizeof(sysrootW) - sizeof(WCHAR)), "wrong return string %s\n", wine_dbgstr_w(buffW)); +} START_TEST(path) { @@ -1471,6 +1546,8 @@ START_TEST(path) pPathIsValidCharA = (void*)GetProcAddress(hShlwapi, (LPSTR)455); pPathIsValidCharW = (void*)GetProcAddress(hShlwapi, (LPSTR)456); pPathAppendA = (void*)GetProcAddress(hShlwapi, "PathAppendA"); + pPathUnExpandEnvStringsA = (void*)GetProcAddress(hShlwapi, "PathUnExpandEnvStringsA"); + pPathUnExpandEnvStringsW = (void*)GetProcAddress(hShlwapi, "PathUnExpandEnvStringsW"); test_PathSearchAndQualify(); test_PathCreateFromUrl(); @@ -1492,4 +1569,5 @@ START_TEST(path) test_PathCommonPrefixA(); test_PathUnquoteSpaces(); test_PathGetDriveNumber(); + test_PathUnExpandEnvStrings(); }