Zac Brown wrote:
Implement SHGetNewLinkInfo[AW].
Fixes Bug 8082 (http://bugs.winehq.org/show_bug.cgi?id=8082)
Changes: * Implement SHGetNewLinkInfo[AW] * Update tests
------------------------------------------------------------------------
--- dlls/shell32/shellord.c | 255 ++++++++++++++++++++++++++++++++++++++++- dlls/shell32/tests/shellord.c | 40 +++---- 2 files changed, 269 insertions(+), 26 deletions(-)
diff --git a/dlls/shell32/shellord.c b/dlls/shell32/shellord.c index 5d1666b..5b1f824 100644 --- a/dlls/shell32/shellord.c +++ b/dlls/shell32/shellord.c @@ -4,6 +4,7 @@ * * Copyright 1997 Marcus Meissner * 1998 Jürgen Schmied + * 2008 Google (Zac Brown) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -1899,22 +1900,270 @@ BOOL WINAPI SHObjectProperties(HWND hwnd, DWORD dwType, LPCWSTR szObject, LPCWST return TRUE; }
+/************************************************************************* + * SHGetNewLinkInfoA [SHELL32.179] + * + * See SHGetNewLinkInfoW + */ BOOL WINAPI SHGetNewLinkInfoA(LPCSTR pszLinkTo, LPCSTR pszDir, LPSTR pszName, BOOL *pfMustCopy, UINT uFlags) { - FIXME("%s, %s, %p, %p, 0x%08x - stub\n", debugstr_a(pszLinkTo), debugstr_a(pszDir), + BOOL ret; + CHAR pathA[MAX_PATH]; + WCHAR pszLinkToW[MAX_PATH]; + WCHAR pszDirW[MAX_PATH]; + WCHAR pszNameW[MAX_PATH]; + LPSTR tmp_ptr; + + TRACE("%s, %s, %p, %p, 0x%08x - stub\n", debugstr_a(pszLinkTo), debugstr_a(pszDir), pszName, pfMustCopy, uFlags);
+ if (pszLinkTo == NULL || pszName == NULL) + return FALSE; + + if (uFlags & SHGNLI_PIDL) + { + if (SHGetPathFromIDListA (pszLinkTo, pathA) == FALSE) + return FALSE; + tmp_ptr = pathA; + } + else + tmp_ptr = pszLinkTo; + + + if (MultiByteToWideChar(CP_ACP, 0, pszName, -1, pszNameW, MAX_PATH) == 0 || + MultiByteToWideChar(CP_ACP, 0, tmp_ptr, -1, pszLinkToW, MAX_PATH) == 0) + return FALSE; + + if (pszDir != NULL) + { + if (MultiByteToWideChar(CP_ACP, 0, pszDir, -1, pszDirW, MAX_PATH) == 0) + return FALSE; + ret = SHGetNewLinkInfoW (pszLinkToW, pszDirW, pszNameW, pfMustCopy, uFlags); + } + else + ret = SHGetNewLinkInfoW (pszLinkToW, NULL, pszNameW, pfMustCopy, uFlags); + + if (WideCharToMultiByte(CP_ACP, 0, pszNameW, -1, pszName, MAX_PATH, 0, 0) && ret == TRUE) + return ret; + return FALSE; }
+/***************************************************************************************** + * [INTERNAL] + * confirm_link_unique: Check if a supplied shortcut name is unique + * within a directory and if not, generate one + * that is for SHGetNewLinkInfoW. + * + * PARAMS + * directory [I] Directory to check for duplicates in + * filename [I] Name of file that a link is being made to + * flags [I] Flags passed to SHGetNewLinkInfoW + * shortcut_name [I/O] Shortcut name to check for. Could be modified + * if needed. + * name_updated [O] Set to true when the name of the shortcut has + * been updated. + * + * RETURNS + * Success: TRUE - The function successfully completed + * Failure: FALSE - The function was unable to complete its task + */ + +static BOOL confirm_link_unique(LPCWSTR directory, LPCWSTR filename, UINT flags, LPWSTR shortcut_name) +{ + static const WCHAR search_pattern[] = {'\\','*',0}; + static const WCHAR prefix_start[] = {'S','h','o','r','t','c','u','t',' ',0}; + static const WCHAR lnk_extension[] = {'.','l','n','k',0}; + static const WCHAR prefix_format[] = {'S','h','o','r','t','c','u','t',' ','(','%','d',')',' ','t','o',' ','%','s',0}; + static const WCHAR no_prefix_format[] = {'%','s',' ','(','%','d',')',0}; + WCHAR filename_noext[MAX_PATH]; + WCHAR tmp_buf[MAX_PATH]; + WCHAR *tmp_ptr, *tmp_ptr2; + HANDLE h; + WIN32_FIND_DATAW find_data; + long shortcut_num = 1; + + /* Get name of file without the file extension */ + tmp_ptr = strrchrW (filename, '.'); + if (tmp_ptr != NULL) + { + if (lstrcpynW (filename_noext, filename, (tmp_ptr - filename)*sizeof(WCHAR)) == NULL) + return FALSE; + } + else + lstrcpyW (filename_noext, filename); + + if (directory != NULL) + { + if (lstrcpyW (tmp_buf, directory) == NULL) + return FALSE; + } + else + { + if (GetCurrentDirectoryW (MAX_PATH, tmp_buf) == 0) + return FALSE; + } + + if (lstrcatW (tmp_buf, search_pattern) == NULL) + return FALSE; + + h = FindFirstFileW (tmp_buf, &find_data); + if (h == INVALID_HANDLE_VALUE) + return FALSE; + + while (FindNextFileW (h, &find_data)) + { + /* Skip if the filename doesn't even contain our shortcut's + target filename */ + if (StrStrW (find_data.cFileName, filename_noext) == NULL) + continue; + + /* Skip if the prefix name flag is set, but no prefix is found + in the file's name. */ + if (flags & SHGNLI_PREFIXNAME) + { + if (StrStrW (find_data.cFileName, prefix_start) == NULL) + continue; + } + + if(lstrcmpW (find_data.cFileName, shortcut_name) == 0) + shortcut_num++; + else + { + tmp_ptr = strchrW (find_data.cFileName, '('); + tmp_ptr2 = strchrW (find_data.cFileName, ')'); + if (tmp_ptr && tmp_ptr2) + { + tmp_ptr++; + tmp_ptr2++; + lstrcpynW (tmp_buf, tmp_ptr, tmp_ptr2 - tmp_ptr); + shortcut_num = wcstol(tmp_buf, &tmp_buf[lstrlenW(tmp_buf)-1], 0) + 1; + } + } + } + + FindClose (h); + + if (shortcut_num > 1) + { + if (flags & SHGNLI_PREFIXNAME) + { + sprintfW (shortcut_name, prefix_format, shortcut_num, filename); + } + else + { + lstrcpyW (shortcut_name, filename); + wsprintfW (shortcut_name, no_prefix_format, filename, shortcut_num); + } + + if ((flags & SHGNLI_NOLNK) == 0) + lstrcatW (shortcut_name, lnk_extension); + } + + return TRUE; +} + +/************************************************************************* + * SHGetNewLinkInfoW [SHELL32.180] + * + * Creates the proper name for a new shortcut. This function does not + * actually create a shortcut. + */ BOOL WINAPI SHGetNewLinkInfoW(LPCWSTR pszLinkTo, LPCWSTR pszDir, LPWSTR pszName, BOOL *pfMustCopy, UINT uFlags) { - FIXME("%s, %s, %p, %p, 0x%08x - stub\n", debugstr_w(pszLinkTo), debugstr_w(pszDir), + static const WCHAR prefix_start[] = {'S', 'h', 'o', 'r', 't', 'c', 'u', 't', ' ', 't', 'o', ' ', 0}; + static const WCHAR prefix_start_nounique[] = + {'S', 'h', 'o', 'r', 't', 'c', 'u', 't', ' ', '(', ')', ' ', 't', 'o', ' ', 0}; + static const WCHAR link_extension[] = {'.', 'l', 'n', 'k', 0}; + static const WCHAR back_slash[] = {'\\',0}; + WCHAR target[MAX_PATH]; + WCHAR shortcut_name[MAX_PATH]; + WCHAR file_name[MAX_PATH]; + LPCWSTR tmp_ptr; + SHFILEINFOW file_info; + BOOL shortcut_name_updated; + + TRACE("%s, %s, %p, %p, 0x%08x - stub\n", debugstr_w(pszLinkTo), debugstr_w(pszDir), pszName, pfMustCopy, uFlags);
- return FALSE; + if (pfMustCopy) + *pfMustCopy = FALSE; + else + return FALSE; + + if (pszLinkTo == NULL || pszName == NULL) + return FALSE; + + if (uFlags & SHGNLI_PIDL) + { + if (SHGetPathFromIDListW (pszLinkTo, target) == FALSE) + return FALSE; + } + else + { + if (lstrcpyW (target, pszLinkTo) == NULL) + return FALSE; + } + + /* Determine the file's name. */ + tmp_ptr = strrchrW (target, '\\'); + if (tmp_ptr != NULL) + tmp_ptr++; + else + tmp_ptr = target; + + lstrcpyW (file_name, tmp_ptr); + + if (uFlags & SHGNLI_NOUNIQUE) + tmp_ptr = prefix_start_nounique; + else + tmp_ptr = prefix_start; + + /* Begin checking against flags and generating the shortcut name. */ + if (uFlags & SHGNLI_PREFIXNAME) + { + lstrcpyW (shortcut_name, tmp_ptr); + lstrcatW (shortcut_name, file_name); + } + else + lstrcpyW (shortcut_name, file_name); + + if ((uFlags & SHGNLI_NOLNK) == 0) + lstrcatW (shortcut_name, link_extension); + + /* Check if we need to generate a unique name. */ + if (lstrcmpW (shortcut_name, file_name) == 0 && !(uFlags & (SHGNLI_NOLNK | SHGNLI_NOUNIQUE))) + { + if (confirm_link_unique (pszDir, file_name, uFlags, shortcut_name) == FALSE) + return FALSE; + } + + if (!(uFlags & SHGNLI_NOUNIQUE)) + { + if (confirm_link_unique (pszDir, file_name, uFlags, shortcut_name) == FALSE) + return FALSE; + } + + if (pszDir != NULL && (uFlags & SHGNLI_NOUNIQUE) == 0) + { + lstrcpyW (pszName, pszDir); + lstrcatW (pszName, back_slash); + lstrcatW (pszName, shortcut_name); + } + else + lstrcpyW (pszName, shortcut_name); + + if (SHGetFileInfoW (target, 0, &file_info, sizeof(file_info), SHGFI_ATTRIBUTES)) + { + if (file_info.dwAttributes & SFGAO_LINK) + *pfMustCopy = TRUE; + else + *pfMustCopy = FALSE; + } + + return TRUE; }
HRESULT WINAPI SHStartNetConnectionDialog(HWND hwnd, LPCSTR pszRemoteName, DWORD dwType) diff --git a/dlls/shell32/tests/shellord.c b/dlls/shell32/tests/shellord.c index f5ae1bd..423ed7d 100644 --- a/dlls/shell32/tests/shellord.c +++ b/dlls/shell32/tests/shellord.c @@ -57,7 +57,7 @@ static const getlink_test_t getlink_tests[] = { "testfile.lnk", "testfile.txt.lnk", TRUE, - TRUE, + FALSE, FALSE }, { @@ -75,7 +75,7 @@ static const getlink_test_t getlink_tests[] = { "testfile.lnk", "testfile.txt.lnk", FALSE, - TRUE, + FALSE, FALSE }, { @@ -84,7 +84,7 @@ static const getlink_test_t getlink_tests[] = { "Shortcut to testfile.lnk", "Shortcut to testfile.txt.lnk", TRUE, - TRUE, + FALSE, TRUE }, { @@ -93,7 +93,7 @@ static const getlink_test_t getlink_tests[] = { "testfile.lnk", "testfile.txt (2)", TRUE, - TRUE, + FALSE, FALSE }, { @@ -129,7 +129,7 @@ static const getlink_test_t getlink_tests[] = { "Shortcut () to testfile.lnk", "Shortcut () to testfile.txt.lnk", FALSE, - TRUE, + FALSE, FALSE }, { @@ -138,7 +138,7 @@ static const getlink_test_t getlink_tests[] = { "testfile.lnk", "testfile.txt", FALSE, - TRUE, + FALSE, FALSE }, { @@ -147,7 +147,7 @@ static const getlink_test_t getlink_tests[] = { "Shortcut to testfile.lnk", "Shortcut to testfile.txt", TRUE, - TRUE, + FALSE, TRUE }, { @@ -174,7 +174,7 @@ static const getlink_test_t getlink_tests[] = { "Shortcut () to testfile.lnk", "Shortcut () to testfile.txt", FALSE, - TRUE, + FALSE, FALSE }, { @@ -237,41 +237,38 @@ static void test_SHGetNewLinkInfo (void) /* Test with all NULL/0 values except for pfMustCopy */ ret = SHGetNewLinkInfoA (NULL, NULL, NULL, &pfMustCopy, flags); ok (ret == FALSE, "Expected return value of FALSE.\n"); - todo_wine ok(pfMustCopy == FALSE, "Expected pfMustCopy to be FALSE.\n"); + ok(pfMustCopy == FALSE, "Expected pfMustCopy to be FALSE.\n");
/* Test with valid target, NULL shortcut directory and name buffers */ if (test_ptr->skip_crash == FALSE) { ret = SHGetNewLinkInfoA (shortcut_ptr, NULL, NULL, &pfMustCopy, flags); ok (ret == FALSE, "Expected return value of FALSE.\n"); - todo_wine ok (pfMustCopy == FALSE, "Expected pfMustCopy to be FALSE.\n"); + ok (pfMustCopy == FALSE, "Expected pfMustCopy to be FALSE.\n"); }
/* Test with valid shortcut directory, NULL target and name buffers */ ret = SHGetNewLinkInfoA (NULL, shortcut_dir, NULL, &pfMustCopy, flags); ok (ret == FALSE, "Expected return value of FALSE.\n"); - todo_wine ok (pfMustCopy == FALSE, "Expected pfMustCopy to be FALSE.\n"); + ok (pfMustCopy == FALSE, "Expected pfMustCopy to be FALSE.\n");
/* Test with valid shortcut name and NULL target, shortcut directory buffers */ ret = SHGetNewLinkInfoA (NULL, NULL, shortcut_name, &pfMustCopy, flags); ok (ret == FALSE, "Expected return value of FALSE.\n"); - todo_wine ok (pfMustCopy == FALSE, "Expected pfMustCopy to be FALSE.\n"); + ok (pfMustCopy == FALSE, "Expected pfMustCopy to be FALSE.\n");
/* Test with valid shortcut directory and name, NULL shortcut target buffer */ ret = SHGetNewLinkInfoA (NULL, shortcut_dir, shortcut_name, &pfMustCopy, flags); ok (ret == FALSE, "Expected return value of FALSE.\n"); - todo_wine ok (pfMustCopy == FALSE, "Expected pfMustCopy to be FALSE.\n"); + ok (pfMustCopy == FALSE, "Expected pfMustCopy to be FALSE.\n");
/* Test with valid shortcut target and name, NULL shortcut directory buffer */ if (test_ptr->skip_crash == FALSE) { memset (shortcut_name, 0, MAX_PATH); ret = SHGetNewLinkInfoA (shortcut_ptr, NULL, shortcut_name, &pfMustCopy, flags); - todo_wine - { - ok (ret == TRUE, "Expected return value of TRUE.\n"); - ok (pfMustCopy == FALSE, "Expected pfMustCopy to be FALSE.\n"); - } + ok (ret == TRUE, "Expected return value of TRUE.\n"); + ok (pfMustCopy == FALSE, "Expected pfMustCopy to be FALSE.\n");
lstrcpyA (tmp_buf1, test_ptr->expected_str_1); lstrcpyA (tmp_buf2, test_ptr->expected_str_2); @@ -299,11 +296,8 @@ static void test_SHGetNewLinkInfo (void) /* Test with valid shortcut directory, target, name buffers */ memset (shortcut_name, 0, MAX_PATH); ret = SHGetNewLinkInfoA (shortcut_ptr, shortcut_dir, shortcut_name, &pfMustCopy, flags); - todo_wine - { - ok (ret == TRUE, "Expected return value of TRUE.\n"); - ok (pfMustCopy == FALSE, "Expected pfMustCopy to be FALSE.\n"); - } + ok (ret == TRUE, "Expected return value of TRUE.\n"); + ok (pfMustCopy == FALSE, "Expected pfMustCopy to be FALSE.\n");
if (test_ptr->use_full_path == TRUE && shortcut_dir != NULL) {
------------------------------------------------------------------------
Ignore this patch, will send a fixed on in a minute.