ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

shlfileop.cpp
Go to the documentation of this file.
00001 /*
00002  * SHFileOperation
00003  *
00004  * Copyright 2000 Juergen Schmied
00005  * Copyright 2002 Andriy Palamarchuk
00006  * Copyright 2004 Dietrich Teickner (from Odin)
00007  * Copyright 2004 Rolf Kalbermatter
00008  *
00009  * This library is free software; you can redistribute it and/or
00010  * modify it under the terms of the GNU Lesser General Public
00011  * License as published by the Free Software Foundation; either
00012  * version 2.1 of the License, or (at your option) any later version.
00013  *
00014  * This library is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017  * Lesser General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU Lesser General Public
00020  * License along with this library; if not, write to the Free Software
00021  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00022  */
00023 
00024 //#define NO_SHLWAPI_STREAM
00025 #include <precomp.h>
00026 
00027 WINE_DEFAULT_DEBUG_CHANNEL(shell);
00028 
00029 #define IsAttrib(x, y)  ((INVALID_FILE_ATTRIBUTES != (x)) && ((x) & (y)))
00030 #define IsAttribFile(x) (!((x) & FILE_ATTRIBUTE_DIRECTORY))
00031 #define IsAttribDir(x)  IsAttrib(x, FILE_ATTRIBUTE_DIRECTORY)
00032 #define IsDotDir(x)     ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
00033 
00034 #define FO_MASK         0xF
00035 #define WM_FILE       (WM_USER + 1)
00036 #define TIMER_ID      (100)
00037 
00038 static const WCHAR wWildcardFile[] = {'*',0};
00039 static const WCHAR wWildcardChars[] = {'*','?',0};
00040 
00041 static DWORD SHNotifyCreateDirectoryW(LPCWSTR path, LPSECURITY_ATTRIBUTES sec);
00042 static DWORD SHNotifyRemoveDirectoryW(LPCWSTR path);
00043 static DWORD SHNotifyDeleteFileW(LPCWSTR path);
00044 static DWORD SHNotifyMoveFileW(LPCWSTR src, LPCWSTR dest);
00045 static DWORD SHNotifyCopyFileW(LPCWSTR src, LPCWSTR dest, BOOL bFailIfExists);
00046 static DWORD SHFindAttrW(LPCWSTR pName, BOOL fileOnly);
00047 
00048 typedef struct
00049 {
00050     SHFILEOPSTRUCTW *req;
00051     DWORD dwYesToAllMask;
00052     BOOL bManyItems;
00053     BOOL bCancelled;
00054 } FILE_OPERATION;
00055 
00056 #define ERROR_SHELL_INTERNAL_FILE_NOT_FOUND 1026
00057 
00058 typedef struct
00059 {
00060     DWORD attributes;
00061     LPWSTR szDirectory;
00062     LPWSTR szFilename;
00063     LPWSTR szFullPath;
00064     BOOL bFromWildcard;
00065     BOOL bFromRelative;
00066     BOOL bExists;
00067 } FILE_ENTRY;
00068 
00069 typedef struct
00070 {
00071     FILE_ENTRY *feFiles;
00072     DWORD num_alloc;
00073     DWORD dwNumFiles;
00074     BOOL bAnyFromWildcard;
00075     BOOL bAnyDirectories;
00076     BOOL bAnyDontExist;
00077 } FILE_LIST;
00078 
00079 typedef struct
00080 {
00081     FILE_LIST * from;
00082     FILE_LIST * to;
00083     FILE_OPERATION * op;
00084     DWORD Index;
00085     HWND hDlgCtrl;
00086     HWND hwndDlg;
00087 }FILE_OPERATION_CONTEXT;
00088 
00089 
00090 /* Confirm dialogs with an optional "Yes To All" as used in file operations confirmations
00091  */
00092 static const WCHAR CONFIRM_MSG_PROP[] = {'W','I','N','E','_','C','O','N','F','I','R','M',0};
00093 
00094 struct confirm_msg_info
00095 {
00096     LPWSTR lpszText;
00097     LPWSTR lpszCaption;
00098     HICON hIcon;
00099     BOOL bYesToAll;
00100 };
00101 
00102 /* as some buttons may be hidden and the dialog height may change we may need
00103  * to move the controls */
00104 static void confirm_msg_move_button(HWND hDlg, INT iId, INT *xPos, INT yOffset, BOOL bShow)
00105 {
00106     HWND hButton = GetDlgItem(hDlg, iId);
00107     RECT r;
00108 
00109     if (bShow)
00110     {
00111         POINT pt;
00112         int width;
00113 
00114         GetWindowRect(hButton, &r);
00115         width = r.right - r.left;
00116         pt.x = r.left;
00117         pt.y = r.top;
00118         ScreenToClient(hDlg, &pt);
00119         MoveWindow(hButton, *xPos - width, pt.y - yOffset, width, r.bottom - r.top, FALSE);
00120         *xPos -= width + 5;
00121     }
00122     else
00123         ShowWindow(hButton, SW_HIDE);
00124 }
00125 
00126 /* Note: we paint the text manually and don't use the static control to make
00127  * sure the text has the same height as the one computed in WM_INITDIALOG
00128  */
00129 static INT_PTR ConfirmMsgBox_Paint(HWND hDlg)
00130 {
00131     PAINTSTRUCT ps;
00132     HFONT hOldFont;
00133     RECT r;
00134     HDC hdc;
00135 
00136     BeginPaint(hDlg, &ps);
00137     hdc = ps.hdc;
00138 
00139     GetClientRect(GetDlgItem(hDlg, IDC_YESTOALL_MESSAGE), &r);
00140     /* this will remap the rect to dialog coords */
00141     MapWindowPoints(GetDlgItem(hDlg, IDC_YESTOALL_MESSAGE), hDlg, (LPPOINT)&r, 2);
00142     hOldFont = (HFONT)SelectObject(hdc, (HFONT)SendDlgItemMessageW(hDlg, IDC_YESTOALL_MESSAGE, WM_GETFONT, 0, 0));
00143     DrawTextW(hdc, (LPWSTR)GetPropW(hDlg, CONFIRM_MSG_PROP), -1, &r, DT_NOPREFIX | DT_PATH_ELLIPSIS | DT_WORDBREAK);
00144     SelectObject(hdc, hOldFont);
00145     EndPaint(hDlg, &ps);
00146 
00147     return TRUE;
00148 }
00149 
00150 static INT_PTR ConfirmMsgBox_Init(HWND hDlg, LPARAM lParam)
00151 {
00152     struct confirm_msg_info *info = (struct confirm_msg_info *)lParam;
00153     INT xPos, yOffset;
00154     int width, height;
00155     HFONT hOldFont;
00156     HDC hdc;
00157     RECT r;
00158 
00159     SetWindowTextW(hDlg, info->lpszCaption);
00160     ShowWindow(GetDlgItem(hDlg, IDC_YESTOALL_MESSAGE), SW_HIDE);
00161     SetPropW(hDlg, CONFIRM_MSG_PROP, info->lpszText);
00162     SendDlgItemMessageW(hDlg, IDC_YESTOALL_ICON, STM_SETICON, (WPARAM)info->hIcon, 0);
00163 
00164     /* compute the text height and resize the dialog */
00165     GetClientRect(GetDlgItem(hDlg, IDC_YESTOALL_MESSAGE), &r);
00166     hdc = GetDC(hDlg);
00167     yOffset = r.bottom;
00168     hOldFont = (HFONT)SelectObject(hdc, (HFONT)SendDlgItemMessageW(hDlg, IDC_YESTOALL_MESSAGE, WM_GETFONT, 0, 0));
00169     DrawTextW(hdc, info->lpszText, -1, &r, DT_NOPREFIX | DT_PATH_ELLIPSIS | DT_WORDBREAK | DT_CALCRECT);
00170     SelectObject(hdc, hOldFont);
00171     yOffset -= r.bottom;
00172     yOffset = min(yOffset, 35);  /* don't make the dialog too small */
00173     ReleaseDC(hDlg, hdc);
00174 
00175     GetClientRect(hDlg, &r);
00176     xPos = r.right - 7;
00177     GetWindowRect(hDlg, &r);
00178     width = r.right - r.left;
00179     height = r.bottom - r.top - yOffset;
00180     MoveWindow(hDlg, (GetSystemMetrics(SM_CXSCREEN) - width)/2,
00181         (GetSystemMetrics(SM_CYSCREEN) - height)/2, width, height, FALSE);
00182 
00183     confirm_msg_move_button(hDlg, IDCANCEL,     &xPos, yOffset, info->bYesToAll);
00184     confirm_msg_move_button(hDlg, IDNO,         &xPos, yOffset, TRUE);
00185     confirm_msg_move_button(hDlg, IDC_YESTOALL, &xPos, yOffset, info->bYesToAll);
00186     confirm_msg_move_button(hDlg, IDYES,        &xPos, yOffset, TRUE);
00187 
00188     return TRUE;
00189 }
00190 
00191 static INT_PTR CALLBACK ConfirmMsgBoxProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
00192 {
00193     switch (uMsg)
00194     {
00195         case WM_INITDIALOG:
00196             return ConfirmMsgBox_Init(hDlg, lParam);
00197         case WM_PAINT:
00198             return ConfirmMsgBox_Paint(hDlg);
00199         case WM_COMMAND:
00200             EndDialog(hDlg, wParam);
00201             break;
00202         case WM_CLOSE:
00203             EndDialog(hDlg, IDCANCEL);
00204             break;
00205     }
00206     return FALSE;
00207 }
00208 
00209 int SHELL_ConfirmMsgBox(HWND hWnd, LPWSTR lpszText, LPWSTR lpszCaption, HICON hIcon, BOOL bYesToAll)
00210 {
00211     struct confirm_msg_info info;
00212 
00213     info.lpszText = lpszText;
00214     info.lpszCaption = lpszCaption;
00215     info.hIcon = hIcon;
00216     info.bYesToAll = bYesToAll;
00217     return DialogBoxParamW(shell32_hInstance, MAKEINTRESOURCEW(IDD_YESTOALL_MSGBOX), hWnd, ConfirmMsgBoxProc, (LPARAM)&info);
00218 }
00219 
00220 /* confirmation dialogs content */
00221 typedef struct
00222 {
00223     HINSTANCE hIconInstance;
00224     UINT icon_resource_id;
00225     UINT caption_resource_id, text_resource_id;
00226 } SHELL_ConfirmIDstruc;
00227 
00228 static BOOL SHELL_ConfirmIDs(int nKindOfDialog, SHELL_ConfirmIDstruc *ids)
00229 {
00230     ids->hIconInstance = shell32_hInstance;
00231     switch (nKindOfDialog)
00232     {
00233         case ASK_DELETE_FILE:
00234             ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE;
00235             ids->caption_resource_id  = IDS_DELETEITEM_CAPTION;
00236             ids->text_resource_id  = IDS_DELETEITEM_TEXT;
00237             return TRUE;
00238 
00239         case ASK_DELETE_FOLDER:
00240             ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE;
00241             ids->caption_resource_id  = IDS_DELETEFOLDER_CAPTION;
00242             ids->text_resource_id  = IDS_DELETEITEM_TEXT;
00243             return TRUE;
00244 
00245         case ASK_DELETE_MULTIPLE_ITEM:
00246             ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE;
00247             ids->caption_resource_id  = IDS_DELETEITEM_CAPTION;
00248             ids->text_resource_id  = IDS_DELETEMULTIPLE_TEXT;
00249             return TRUE;
00250 
00251         case ASK_TRASH_FILE:
00252             ids->icon_resource_id = IDI_SHELL_TRASH_FILE;
00253             ids->caption_resource_id = IDS_DELETEITEM_CAPTION;
00254             ids->text_resource_id = IDS_TRASHITEM_TEXT;
00255             return TRUE;
00256 
00257         case ASK_TRASH_FOLDER:
00258             ids->icon_resource_id = IDI_SHELL_TRASH_FILE;
00259             ids->caption_resource_id = IDS_DELETEFOLDER_CAPTION;
00260             ids->text_resource_id = IDS_TRASHFOLDER_TEXT;
00261             return TRUE;
00262 
00263         case ASK_TRASH_MULTIPLE_ITEM:
00264             ids->icon_resource_id = IDI_SHELL_TRASH_FILE;
00265             ids->caption_resource_id = IDS_DELETEITEM_CAPTION;
00266             ids->text_resource_id = IDS_TRASHMULTIPLE_TEXT;
00267             return TRUE;
00268 
00269         case ASK_CANT_TRASH_ITEM:
00270             ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE;
00271             ids->caption_resource_id  = IDS_DELETEITEM_CAPTION;
00272             ids->text_resource_id  = IDS_CANTTRASH_TEXT;
00273             return TRUE;
00274 
00275         case ASK_DELETE_SELECTED:
00276             ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE;
00277             ids->caption_resource_id  = IDS_DELETEITEM_CAPTION;
00278             ids->text_resource_id  = IDS_DELETESELECTED_TEXT;
00279             return TRUE;
00280 
00281       case ASK_OVERWRITE_FILE:
00282             ids->hIconInstance = NULL;
00283             ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE;
00284             ids->caption_resource_id  = IDS_OVERWRITEFILE_CAPTION;
00285             ids->text_resource_id  = IDS_OVERWRITEFILE_TEXT;
00286             return TRUE;
00287 
00288       case ASK_OVERWRITE_FOLDER:
00289             ids->hIconInstance = NULL;
00290             ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE;
00291             ids->caption_resource_id  = IDS_OVERWRITEFILE_CAPTION;
00292             ids->text_resource_id  = IDS_OVERWRITEFOLDER_TEXT;
00293             return TRUE;
00294 
00295         default:
00296             FIXME(" Unhandled nKindOfDialog %d stub\n", nKindOfDialog);
00297     }
00298     return FALSE;
00299 }
00300 
00301 static BOOL SHELL_ConfirmDialogW(HWND hWnd, int nKindOfDialog, LPCWSTR szDir, FILE_OPERATION *op)
00302 {
00303     WCHAR szCaption[255], szText[255], szBuffer[MAX_PATH + 256];
00304     SHELL_ConfirmIDstruc ids;
00305     DWORD_PTR args[1];
00306     HICON hIcon;
00307     int ret;
00308 
00309     assert(nKindOfDialog >= 0 && nKindOfDialog < 32);
00310     if (op && (op->dwYesToAllMask & (1 << nKindOfDialog)))
00311         return TRUE;
00312 
00313     if (!SHELL_ConfirmIDs(nKindOfDialog, &ids)) return FALSE;
00314 
00315     LoadStringW(shell32_hInstance, ids.caption_resource_id, szCaption, sizeof(szCaption)/sizeof(WCHAR));
00316     LoadStringW(shell32_hInstance, ids.text_resource_id, szText, sizeof(szText)/sizeof(WCHAR));
00317 
00318     args[0] = (DWORD_PTR)szDir;
00319     FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
00320                    szText, 0, 0, szBuffer, sizeof(szBuffer), (va_list*)args);
00321     hIcon = LoadIconW(ids.hIconInstance, (LPWSTR)MAKEINTRESOURCE(ids.icon_resource_id));
00322 
00323     ret = SHELL_ConfirmMsgBox(hWnd, szBuffer, szCaption, hIcon, op && op->bManyItems);
00324     if (op)
00325     {
00326         if (ret == IDC_YESTOALL)
00327         {
00328             op->dwYesToAllMask |= (1 << nKindOfDialog);
00329             ret = IDYES;
00330         }
00331         if (ret == IDCANCEL)
00332             op->bCancelled = TRUE;
00333         if (ret != IDYES)
00334             op->req->fAnyOperationsAborted = TRUE;
00335     }
00336     return ret == IDYES;
00337 }
00338 
00339 BOOL SHELL_ConfirmYesNoW(HWND hWnd, int nKindOfDialog, LPCWSTR szDir)
00340 {
00341     return SHELL_ConfirmDialogW(hWnd, nKindOfDialog, szDir, NULL);
00342 }
00343 
00344 static DWORD SHELL32_AnsiToUnicodeBuf(LPCSTR aPath, LPWSTR *wPath, DWORD minChars)
00345 {
00346     DWORD len = MultiByteToWideChar(CP_ACP, 0, aPath, -1, NULL, 0);
00347 
00348     if (len < minChars)
00349       len = minChars;
00350 
00351     *wPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
00352     if (*wPath)
00353     {
00354       MultiByteToWideChar(CP_ACP, 0, aPath, -1, *wPath, len);
00355       return NO_ERROR;
00356     }
00357     return E_OUTOFMEMORY;
00358 }
00359 
00360 static void SHELL32_FreeUnicodeBuf(LPWSTR wPath)
00361 {
00362     HeapFree(GetProcessHeap(), 0, wPath);
00363 }
00364 
00365 EXTERN_C HRESULT WINAPI SHIsFileAvailableOffline(LPCWSTR path, LPDWORD status)
00366 {
00367     FIXME("(%s, %p) stub\n", debugstr_w(path), status);
00368     return E_FAIL;
00369 }
00370 
00371 /**************************************************************************
00372  * SHELL_DeleteDirectory()  [internal]
00373  *
00374  * Asks for confirmation when bShowUI is true and deletes the directory and
00375  * all its subdirectories and files if necessary.
00376  */
00377 BOOL SHELL_DeleteDirectoryW(HWND hwnd, LPCWSTR pszDir, BOOL bShowUI)
00378 {
00379     BOOL    ret = TRUE;
00380     HANDLE  hFind;
00381     WIN32_FIND_DATAW wfd;
00382     WCHAR   szTemp[MAX_PATH];
00383 
00384     /* Make sure the directory exists before eventually prompting the user */
00385     PathCombineW(szTemp, pszDir, wWildcardFile);
00386     hFind = FindFirstFileW(szTemp, &wfd);
00387     if (hFind == INVALID_HANDLE_VALUE)
00388       return FALSE;
00389 
00390     if (!bShowUI || (ret = SHELL_ConfirmDialogW(hwnd, ASK_DELETE_FOLDER, pszDir, NULL)))
00391     {
00392         do
00393         {
00394             if (IsDotDir(wfd.cFileName))
00395                 continue;
00396             PathCombineW(szTemp, pszDir, wfd.cFileName);
00397             if (FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes)
00398                 ret = SHELL_DeleteDirectoryW(hwnd, szTemp, FALSE);
00399             else
00400                 ret = (SHNotifyDeleteFileW(szTemp) == ERROR_SUCCESS);
00401         } while (ret && FindNextFileW(hFind, &wfd));
00402     }
00403     FindClose(hFind);
00404     if (ret)
00405         ret = (SHNotifyRemoveDirectoryW(pszDir) == ERROR_SUCCESS);
00406     return ret;
00407 }
00408 
00409 /**************************************************************************
00410  * Win32CreateDirectory      [SHELL32.93]
00411  *
00412  * Creates a directory. Also triggers a change notify if one exists.
00413  *
00414  * PARAMS
00415  *  path       [I]   path to directory to create
00416  *
00417  * RETURNS
00418  *  TRUE if successful, FALSE otherwise
00419  */
00420 
00421 static DWORD SHNotifyCreateDirectoryW(LPCWSTR path, LPSECURITY_ATTRIBUTES sec)
00422 {
00423     TRACE("(%s, %p)\n", debugstr_w(path), sec);
00424 
00425     if (CreateDirectoryW(path, sec))
00426     {
00427       SHChangeNotify(SHCNE_MKDIR, SHCNF_PATHW, path, NULL);
00428       return ERROR_SUCCESS;
00429     }
00430     return GetLastError();
00431 }
00432 
00433 /**********************************************************************/
00434 
00435 EXTERN_C BOOL WINAPI Win32CreateDirectoryW(LPCWSTR path, LPSECURITY_ATTRIBUTES sec)
00436 {
00437     return (SHNotifyCreateDirectoryW(path, sec) == ERROR_SUCCESS);
00438 }
00439 
00440 /************************************************************************
00441  * Win32RemoveDirectory      [SHELL32.94]
00442  *
00443  * Deletes a directory. Also triggers a change notify if one exists.
00444  *
00445  * PARAMS
00446  *  path       [I]   path to directory to delete
00447  *
00448  * RETURNS
00449  *  TRUE if successful, FALSE otherwise
00450  */
00451 static DWORD SHNotifyRemoveDirectoryW(LPCWSTR path)
00452 {
00453     BOOL ret;
00454     TRACE("(%s)\n", debugstr_w(path));
00455 
00456     ret = RemoveDirectoryW(path);
00457     if (!ret)
00458     {
00459       /* Directory may be write protected */
00460       DWORD dwAttr = GetFileAttributesW(path);
00461       if (IsAttrib(dwAttr, FILE_ATTRIBUTE_READONLY))
00462         if (SetFileAttributesW(path, dwAttr & ~FILE_ATTRIBUTE_READONLY))
00463           ret = RemoveDirectoryW(path);
00464     }
00465     if (ret)
00466     {
00467       SHChangeNotify(SHCNE_RMDIR, SHCNF_PATHW, path, NULL);
00468       return ERROR_SUCCESS;
00469     }
00470     return GetLastError();
00471 }
00472 
00473 /***********************************************************************/
00474 
00475 EXTERN_C BOOL WINAPI Win32RemoveDirectoryW(LPCWSTR path)
00476 {
00477     return (SHNotifyRemoveDirectoryW(path) == ERROR_SUCCESS);
00478 }
00479 
00480 /************************************************************************
00481  * Win32DeleteFile           [SHELL32.164]
00482  *
00483  * Deletes a file. Also triggers a change notify if one exists.
00484  *
00485  * PARAMS
00486  *  path       [I]   path to file to delete
00487  *
00488  * RETURNS
00489  *  TRUE if successful, FALSE otherwise
00490  */
00491 static DWORD SHNotifyDeleteFileW(LPCWSTR path)
00492 {
00493     BOOL ret;
00494 
00495     TRACE("(%s)\n", debugstr_w(path));
00496 
00497     ret = DeleteFileW(path);
00498     if (!ret)
00499     {
00500       /* File may be write protected or a system file */
00501       DWORD dwAttr = GetFileAttributesW(path);
00502       if (IsAttrib(dwAttr, FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM))
00503         if (SetFileAttributesW(path, dwAttr & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)))
00504             ret = DeleteFileW(path);
00505     }
00506     if (ret)
00507     {
00508         SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW, path, NULL);
00509         return ERROR_SUCCESS;
00510     }
00511     return GetLastError();
00512 }
00513 
00514 /***********************************************************************/
00515 
00516 EXTERN_C DWORD WINAPI Win32DeleteFileW(LPCWSTR path)
00517 {
00518     return (SHNotifyDeleteFileW(path) == ERROR_SUCCESS);
00519 }
00520 
00521 /************************************************************************
00522  * SHNotifyMoveFile          [internal]
00523  *
00524  * Moves a file. Also triggers a change notify if one exists.
00525  *
00526  * PARAMS
00527  *  src        [I]   path to source file to move
00528  *  dest       [I]   path to target file to move to
00529  *
00530  * RETURNS
00531  *  ERORR_SUCCESS if successful
00532  */
00533 static DWORD SHNotifyMoveFileW(LPCWSTR src, LPCWSTR dest)
00534 {
00535     BOOL ret;
00536 
00537     TRACE("(%s %s)\n", debugstr_w(src), debugstr_w(dest));
00538 
00539         ret = MoveFileExW(src, dest, MOVEFILE_REPLACE_EXISTING);
00540 
00541         /* MOVEFILE_REPLACE_EXISTING fails with dirs, so try MoveFile */
00542         if (!ret)
00543             ret = MoveFileW(src, dest);
00544 
00545     if (!ret)
00546     {
00547       DWORD dwAttr;
00548 
00549       dwAttr = SHFindAttrW(dest, FALSE);
00550       if (INVALID_FILE_ATTRIBUTES == dwAttr)
00551       {
00552         /* Source file may be write protected or a system file */
00553         dwAttr = GetFileAttributesW(src);
00554         if (IsAttrib(dwAttr, FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM))
00555             if (SetFileAttributesW(src, dwAttr & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)))
00556                 ret = MoveFileW(src, dest);
00557       }
00558     }
00559     if (ret)
00560     {
00561         SHChangeNotify(SHCNE_RENAMEITEM, SHCNF_PATHW, src, dest);
00562         return ERROR_SUCCESS;
00563     }
00564     return GetLastError();
00565 }
00566 
00567 static DWORD WINAPI SHOperationProgressRoutine(LARGE_INTEGER TotalFileSize, LARGE_INTEGER TotalBytesTransferred, LARGE_INTEGER StreamSize, LARGE_INTEGER StreamBytesTransferred, DWORD dwStreamNumber, DWORD dwCallbackReason, HANDLE hSourceFile, HANDLE hDestinationFile, LPVOID lpData)
00568 {
00569     FILE_OPERATION_CONTEXT * Context;
00570     LARGE_INTEGER Progress;
00571 
00572     /* get context */
00573     Context = (FILE_OPERATION_CONTEXT*)lpData;
00574 
00575     if (TotalBytesTransferred.QuadPart)
00576     {
00577         Progress.QuadPart = (TotalBytesTransferred.QuadPart * 100) / TotalFileSize.QuadPart;
00578     }
00579     else
00580     {
00581         Progress.QuadPart = 1;
00582     }
00583 
00584     /* update progress bar */
00585     SendMessageW(Context->hDlgCtrl, PBM_SETPOS, (WPARAM)Progress.u.LowPart, 0);
00586 
00587     if (TotalBytesTransferred.QuadPart == TotalFileSize.QuadPart)
00588     {
00589         /* file was copied */
00590         Context->Index++;
00591         PostMessageW(Context->hwndDlg, WM_FILE, 0, 0);
00592     }
00593 
00594     return PROGRESS_CONTINUE;
00595 }
00596 
00597 BOOL
00598 QueueFile(
00599     FILE_OPERATION_CONTEXT * Context)
00600 {
00601     FILE_ENTRY * from, *to = NULL;
00602     BOOL bRet = FALSE;
00603 
00604     if (Context->Index >= Context->from->dwNumFiles)
00605         return FALSE;
00606 
00607     /* get current file */
00608     from = &Context->from->feFiles[Context->Index];
00609 
00610     if (Context->op->req->wFunc != FO_DELETE)
00611         to = &Context->to->feFiles[Context->Index];
00612 
00613     /* update status */
00614     SetDlgItemTextW(Context->hwndDlg, 14001, from->szFullPath);
00615 
00616     if (Context->op->req->wFunc == FO_COPY)
00617     {
00618         bRet = CopyFileExW(from->szFullPath, to->szFullPath, SHOperationProgressRoutine, (LPVOID)Context, &Context->op->bCancelled, 0);
00619     }
00620     else if (Context->op->req->wFunc == FO_MOVE)
00621     {
00622         //bRet = MoveFileWithProgressW(from->szFullPath, to->szFullPath, SHOperationProgressRoutine, (LPVOID)Context, MOVEFILE_COPY_ALLOWED);
00623     }
00624     else if (Context->op->req->wFunc == FO_DELETE)
00625     {
00626         bRet = DeleteFile(from->szFullPath);
00627     }
00628 
00629     return bRet;
00630 }
00631 
00632 static INT_PTR CALLBACK SHOperationDialog(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
00633 {
00634     FILE_OPERATION_CONTEXT * Context;
00635 
00636     Context = (FILE_OPERATION_CONTEXT*) GetWindowLongPtr(hwndDlg, DWLP_USER);
00637 
00638     switch(uMsg)
00639     {
00640         case WM_INITDIALOG:
00641             SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG)lParam);
00642 
00643             /* get context */
00644             Context = (FILE_OPERATION_CONTEXT*)lParam;
00645 
00646             /* store progress bar handle */
00647             Context->hDlgCtrl = GetDlgItem(hwndDlg, 14000);
00648 
00649             /* store window handle */
00650             Context->hwndDlg = hwndDlg;
00651 
00652             /* set progress bar range */
00653             (void)SendMessageW(Context->hDlgCtrl, (UINT) PBM_SETRANGE, 0, MAKELPARAM(0, 100));
00654 
00655             /* start file queueing */
00656             SetTimer(hwndDlg, TIMER_ID, 1000, NULL);
00657             //QueueFile(Context);
00658 
00659             return TRUE;
00660 
00661         case WM_CLOSE:
00662             Context->op->bCancelled = TRUE;
00663             EndDialog(hwndDlg, Context->op->bCancelled);
00664             return TRUE;
00665 
00666         case WM_COMMAND:
00667             if (LOWORD(wParam) == 14002)
00668             {
00669                 Context->op->bCancelled = TRUE;
00670                 EndDialog(hwndDlg, Context->op->bCancelled);
00671                 return TRUE;
00672             }; break;
00673 
00674         case WM_TIMER:
00675             if (wParam == TIMER_ID)
00676             {
00677                 QueueFile(Context);
00678                 KillTimer(hwndDlg, TIMER_ID);
00679             }; break;
00680 
00681         case WM_FILE:
00682             if (!QueueFile(Context))
00683                 EndDialog(hwndDlg, Context->op->bCancelled);
00684         default:
00685             break;
00686     }
00687     return FALSE;
00688 }
00689 
00690 HRESULT
00691 SHShowFileOperationDialog(FILE_OPERATION *op, FILE_LIST *flFrom, FILE_LIST *flTo)
00692 {
00693     HWND hwnd;
00694     BOOL bRet;
00695     MSG msg;
00696     FILE_OPERATION_CONTEXT Context;
00697 
00698     Context.from = flFrom;
00699     Context.to = flTo;
00700     Context.op = op;
00701     Context.Index = 0;
00702     Context.op->bCancelled = FALSE;
00703 
00704     hwnd = CreateDialogParam(shell32_hInstance, MAKEINTRESOURCE(IDD_FILE_COPY), NULL, SHOperationDialog, (LPARAM)&Context);
00705     if (hwnd == NULL)
00706     {
00707         ERR("Failed to create dialog\n");
00708         return E_FAIL;
00709     }
00710     ShowWindow(hwnd, SW_SHOWNORMAL);
00711 
00712     while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
00713     {
00714         if (!IsWindow(hwnd) || !IsDialogMessage(hwnd, &msg))
00715         {
00716             TranslateMessage(&msg);
00717             DispatchMessage(&msg);
00718         }
00719     }
00720 
00721     return NOERROR;
00722 }
00723 
00724 
00725 /************************************************************************
00726  * SHNotifyCopyFile          [internal]
00727  *
00728  * Copies a file. Also triggers a change notify if one exists.
00729  *
00730  * PARAMS
00731  *  src           [I]   path to source file to move
00732  *  dest          [I]   path to target file to move to
00733  *  bFailIfExists [I]   if TRUE, the target file will not be overwritten if
00734  *                      a file with this name already exists
00735  *
00736  * RETURNS
00737  *  ERROR_SUCCESS if successful
00738  */
00739 static DWORD SHNotifyCopyFileW(LPCWSTR src, LPCWSTR dest, BOOL bFailIfExists)
00740 {
00741     BOOL ret;
00742     DWORD attribs;
00743 
00744     TRACE("(%s %s %s)\n", debugstr_w(src), debugstr_w(dest), bFailIfExists ? "failIfExists" : "");
00745 
00746     /* Destination file may already exist with read only attribute */
00747     attribs = GetFileAttributesW(dest);
00748     if (IsAttrib(attribs, FILE_ATTRIBUTE_READONLY))
00749         SetFileAttributesW(dest, attribs & ~FILE_ATTRIBUTE_READONLY);
00750 
00751     if (GetFileAttributesW(dest) & FILE_ATTRIBUTE_READONLY)
00752     {
00753         SetFileAttributesW(dest, attribs & ~FILE_ATTRIBUTE_READONLY);
00754         if (GetFileAttributesW(dest) & FILE_ATTRIBUTE_READONLY)
00755         {
00756             TRACE("[shell32, SHNotifyCopyFileW] STILL SHIT\n");
00757         }
00758     }
00759 
00760     ret = CopyFileW(src, dest, bFailIfExists);
00761     if (ret)
00762     {
00763         SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, dest, NULL);
00764         return ERROR_SUCCESS;
00765     }
00766 
00767     return GetLastError();
00768 }
00769 
00770 /*************************************************************************
00771  * SHCreateDirectory         [SHELL32.165]
00772  *
00773  * This function creates a file system folder whose fully qualified path is
00774  * given by path. If one or more of the intermediate folders do not exist,
00775  * they will be created as well.
00776  *
00777  * PARAMS
00778  *  hWnd       [I]
00779  *  path       [I]   path of directory to create
00780  *
00781  * RETURNS
00782  *  ERROR_SUCCESS or one of the following values:
00783  *  ERROR_BAD_PATHNAME if the path is relative
00784  *  ERROR_FILE_EXISTS when a file with that name exists
00785  *  ERROR_PATH_NOT_FOUND can't find the path, probably invalid
00786  *  ERROR_INVALID_NAME if the path contains invalid chars
00787  *  ERROR_ALREADY_EXISTS when the directory already exists
00788  *  ERROR_FILENAME_EXCED_RANGE if the filename was to long to process
00789  *
00790  * NOTES
00791  *  exported by ordinal
00792  *  Win9x exports ANSI
00793  *  WinNT/2000 exports Unicode
00794  */
00795 int WINAPI SHCreateDirectory(HWND hWnd, LPCWSTR path)
00796 {
00797      return SHCreateDirectoryExW(hWnd, path, NULL);
00798 }
00799 
00800 /*************************************************************************
00801  * SHCreateDirectoryExA      [SHELL32.@]
00802  *
00803  * This function creates a file system folder whose fully qualified path is
00804  * given by path. If one or more of the intermediate folders do not exist,
00805  * they will be created as well.
00806  *
00807  * PARAMS
00808  *  hWnd       [I]
00809  *  path       [I]   path of directory to create
00810  *  sec        [I]   security attributes to use or NULL
00811  *
00812  * RETURNS
00813  *  ERROR_SUCCESS or one of the following values:
00814  *  ERROR_BAD_PATHNAME or ERROR_PATH_NOT_FOUND if the path is relative
00815  *  ERROR_INVALID_NAME if the path contains invalid chars
00816  *  ERROR_FILE_EXISTS when a file with that name exists
00817  *  ERROR_ALREADY_EXISTS when the directory already exists
00818  *  ERROR_FILENAME_EXCED_RANGE if the filename was to long to process
00819  *
00820  *  FIXME: Not implemented yet;
00821  *  SHCreateDirectoryEx also verifies that the files in the directory will be visible
00822  *  if the path is a network path to deal with network drivers which might have a limited
00823  *  but unknown maximum path length. If not:
00824  *
00825  *  If hWnd is set to a valid window handle, a message box is displayed warning
00826  *  the user that the files may not be accessible. If the user chooses not to
00827  *  proceed, the function returns ERROR_CANCELLED.
00828  *
00829  *  If hWnd is set to NULL, no user interface is displayed and the function
00830  *  returns ERROR_CANCELLED.
00831  */
00832 int WINAPI SHCreateDirectoryExA(HWND hWnd, LPCSTR path, LPSECURITY_ATTRIBUTES sec)
00833 {
00834     LPWSTR wPath;
00835     DWORD retCode;
00836 
00837     TRACE("(%s, %p)\n", debugstr_a(path), sec);
00838 
00839     retCode = SHELL32_AnsiToUnicodeBuf(path, &wPath, 0);
00840     if (!retCode)
00841     {
00842         retCode = SHCreateDirectoryExW(hWnd, wPath, sec);
00843         SHELL32_FreeUnicodeBuf(wPath);
00844     }
00845     return retCode;
00846 }
00847 
00848 /*************************************************************************
00849  * SHCreateDirectoryExW      [SHELL32.@]
00850  *
00851  * See SHCreateDirectoryExA.
00852  */
00853 int WINAPI SHCreateDirectoryExW(HWND hWnd, LPCWSTR path, LPSECURITY_ATTRIBUTES sec)
00854 {
00855     int ret = ERROR_BAD_PATHNAME;
00856     TRACE("(%p, %s, %p)\n", hWnd, debugstr_w(path), sec);
00857 
00858     if (PathIsRelativeW(path))
00859     {
00860       SetLastError(ret);
00861     }
00862     else
00863     {
00864         ret = SHNotifyCreateDirectoryW(path, sec);
00865         /* Refuse to work on certain error codes before trying to create directories recursively */
00866         if (ret != ERROR_SUCCESS &&
00867           ret != ERROR_FILE_EXISTS &&
00868           ret != ERROR_ALREADY_EXISTS &&
00869           ret != ERROR_FILENAME_EXCED_RANGE)
00870         {
00871             WCHAR *pEnd, *pSlash, szTemp[MAX_PATH + 1];  /* extra for PathAddBackslash() */
00872 
00873             lstrcpynW(szTemp, path, MAX_PATH);
00874             pEnd = PathAddBackslashW(szTemp);
00875             pSlash = szTemp + 3;
00876 
00877             while (*pSlash)
00878             {
00879                 while (*pSlash && *pSlash != '\\') pSlash++;
00880                 if (*pSlash)
00881                 {
00882                     *pSlash = 0;    /* terminate path at separator */
00883 
00884                     ret = SHNotifyCreateDirectoryW(szTemp, pSlash + 1 == pEnd ? sec : NULL);
00885                 }
00886                 *pSlash++ = '\\'; /* put the separator back */
00887             }
00888         }
00889 
00890         if (ret && hWnd && (ERROR_CANCELLED != ret))
00891         {
00892             /* We failed and should show a dialog box */
00893             FIXME("Show system error message, creating path %s, failed with error %d\n", debugstr_w(path), ret);
00894             ret = ERROR_CANCELLED; /* Error has been already presented to user (not really yet!) */
00895         }
00896     }
00897 
00898     return ret;
00899 }
00900 
00901 /*************************************************************************
00902  * SHFindAttrW      [internal]
00903  *
00904  * Get the Attributes for a file or directory. The difference to GetAttributes()
00905  * is that this function will also work for paths containing wildcard characters
00906  * in its filename.
00907 
00908  * PARAMS
00909  *  path       [I]   path of directory or file to check
00910  *  fileOnly   [I]   TRUE if only files should be found
00911  *
00912  * RETURNS
00913  *  INVALID_FILE_ATTRIBUTES if the path does not exist, the actual attributes of
00914  *  the first file or directory found otherwise
00915  */
00916 static DWORD SHFindAttrW(LPCWSTR pName, BOOL fileOnly)
00917 {
00918     WIN32_FIND_DATAW wfd;
00919     BOOL b_FileMask = fileOnly && (NULL != StrPBrkW(pName, wWildcardChars));
00920     DWORD dwAttr = INVALID_FILE_ATTRIBUTES;
00921     HANDLE hFind = FindFirstFileW(pName, &wfd);
00922 
00923     TRACE("%s %d\n", debugstr_w(pName), fileOnly);
00924     if (INVALID_HANDLE_VALUE != hFind)
00925     {
00926         do
00927         {
00928             if (b_FileMask && IsAttribDir(wfd.dwFileAttributes))
00929                continue;
00930             dwAttr = wfd.dwFileAttributes;
00931             break;
00932         } while (FindNextFileW(hFind, &wfd));
00933 
00934       FindClose(hFind);
00935     }
00936     return dwAttr;
00937 }
00938 
00939 /*************************************************************************
00940  *
00941  * SHNameTranslate HelperFunction for SHFileOperationA
00942  *
00943  * Translates a list of 0 terminated ASCII strings into Unicode. If *wString
00944  * is NULL, only the necessary size of the string is determined and returned,
00945  * otherwise the ASCII strings are copied into it and the buffer is increased
00946  * to point to the location after the final 0 termination char.
00947  */
00948 static DWORD SHNameTranslate(LPWSTR* wString, LPCWSTR* pWToFrom, BOOL more)
00949 {
00950     DWORD size = 0, aSize = 0;
00951     LPCSTR aString = (LPCSTR)*pWToFrom;
00952 
00953     if (aString)
00954     {
00955         do
00956         {
00957             size = lstrlenA(aString) + 1;
00958             aSize += size;
00959             aString += size;
00960         } while ((size != 1) && more);
00961 
00962         /* The two sizes might be different in the case of multibyte chars */
00963         size = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)*pWToFrom, aSize, *wString, 0);
00964         if (*wString) /* only in the second loop */
00965         {
00966             MultiByteToWideChar(CP_ACP, 0, (LPCSTR)*pWToFrom, aSize, *wString, size);
00967             *pWToFrom = *wString;
00968             *wString += size;
00969         }
00970     }
00971     return size;
00972 }
00973 /*************************************************************************
00974  * SHFileOperationA          [SHELL32.@]
00975  *
00976  * Function to copy, move, delete and create one or more files with optional
00977  * user prompts.
00978  *
00979  * PARAMS
00980  *  lpFileOp   [I/O] pointer to a structure containing all the necessary information
00981  *
00982  * RETURNS
00983  *  Success: ERROR_SUCCESS.
00984  *  Failure: ERROR_CANCELLED.
00985  *
00986  * NOTES
00987  *  exported by name
00988  */
00989 int WINAPI SHFileOperationA(LPSHFILEOPSTRUCTA lpFileOp)
00990 {
00991     SHFILEOPSTRUCTW nFileOp = *((LPSHFILEOPSTRUCTW)lpFileOp);
00992     int retCode = 0;
00993     DWORD size;
00994     LPWSTR ForFree = NULL, /* we change wString in SHNameTranslate and can't use it for freeing */
00995            wString = NULL; /* we change this in SHNameTranslate */
00996 
00997     TRACE("\n");
00998     if (FO_DELETE == (nFileOp.wFunc & FO_MASK))
00999         nFileOp.pTo = NULL; /* we need a NULL or a valid pointer for translation */
01000     if (!(nFileOp.fFlags & FOF_SIMPLEPROGRESS))
01001         nFileOp.lpszProgressTitle = NULL; /* we need a NULL or a valid pointer for translation */
01002     while (1) /* every loop calculate size, second translate also, if we have storage for this */
01003     {
01004         size = SHNameTranslate(&wString, &nFileOp.lpszProgressTitle, FALSE); /* no loop */
01005         size += SHNameTranslate(&wString, &nFileOp.pFrom, TRUE); /* internal loop */
01006         size += SHNameTranslate(&wString, &nFileOp.pTo, TRUE); /* internal loop */
01007 
01008         if (ForFree)
01009         {
01010             retCode = SHFileOperationW(&nFileOp);
01011             HeapFree(GetProcessHeap(), 0, ForFree); /* we cannot use wString, it was changed */
01012             break;
01013         }
01014         else
01015         {
01016             wString = ForFree = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
01017             if (ForFree) continue;
01018             retCode = ERROR_OUTOFMEMORY;
01019             nFileOp.fAnyOperationsAborted = TRUE;
01020             SetLastError(retCode);
01021             return retCode;
01022         }
01023     }
01024 
01025     lpFileOp->hNameMappings = nFileOp.hNameMappings;
01026     lpFileOp->fAnyOperationsAborted = nFileOp.fAnyOperationsAborted;
01027     return retCode;
01028 }
01029 
01030 static void __inline grow_list(FILE_LIST *list)
01031 {
01032     FILE_ENTRY *newx = (FILE_ENTRY *)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, list->feFiles,
01033                                   list->num_alloc * 2 * sizeof(*newx) );
01034     list->feFiles = newx;
01035     list->num_alloc *= 2;
01036 }
01037 
01038 /* adds a file to the FILE_ENTRY struct
01039  */
01040 static void add_file_to_entry(FILE_ENTRY *feFile, LPCWSTR szFile)
01041 {
01042     DWORD dwLen = lstrlenW(szFile) + 1;
01043     LPCWSTR ptr;
01044 
01045     feFile->szFullPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR));
01046     lstrcpyW(feFile->szFullPath, szFile);
01047 
01048     ptr = StrRChrW(szFile, NULL, '\\');
01049     if (ptr)
01050     {
01051         dwLen = ptr - szFile + 1;
01052         feFile->szDirectory = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR));
01053         lstrcpynW(feFile->szDirectory, szFile, dwLen);
01054 
01055         dwLen = lstrlenW(feFile->szFullPath) - dwLen + 1;
01056         feFile->szFilename = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR));
01057         lstrcpyW(feFile->szFilename, ptr + 1); /* skip over backslash */
01058     }
01059     feFile->bFromWildcard = FALSE;
01060 }
01061 
01062 static LPWSTR wildcard_to_file(LPCWSTR szWildCard, LPCWSTR szFileName)
01063 {
01064     LPCWSTR ptr;
01065     LPWSTR szFullPath;
01066     DWORD dwDirLen, dwFullLen;
01067 
01068     ptr = StrRChrW(szWildCard, NULL, '\\');
01069     dwDirLen = ptr - szWildCard + 1;
01070 
01071     dwFullLen = dwDirLen + lstrlenW(szFileName) + 1;
01072     szFullPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwFullLen * sizeof(WCHAR));
01073 
01074     lstrcpynW(szFullPath, szWildCard, dwDirLen + 1);
01075     lstrcatW(szFullPath, szFileName);
01076 
01077     return szFullPath;
01078 }
01079 
01080 static void parse_wildcard_files(FILE_LIST *flList, LPCWSTR szFile, LPDWORD pdwListIndex)
01081 {
01082     WIN32_FIND_DATAW wfd;
01083     HANDLE hFile = FindFirstFileW(szFile, &wfd);
01084     FILE_ENTRY *file;
01085     LPWSTR szFullPath;
01086     BOOL res;
01087 
01088     if (hFile == INVALID_HANDLE_VALUE) return;
01089 
01090     for (res = TRUE; res; res = FindNextFileW(hFile, &wfd))
01091     {
01092         if (IsDotDir(wfd.cFileName))
01093             continue;
01094 
01095         if (*pdwListIndex >= flList->num_alloc)
01096             grow_list( flList );
01097 
01098         szFullPath = wildcard_to_file(szFile, wfd.cFileName);
01099         file = &flList->feFiles[(*pdwListIndex)++];
01100         add_file_to_entry(file, szFullPath);
01101         file->bFromWildcard = TRUE;
01102         file->attributes = wfd.dwFileAttributes;
01103 
01104         if (IsAttribDir(file->attributes))
01105             flList->bAnyDirectories = TRUE;
01106 
01107         HeapFree(GetProcessHeap(), 0, szFullPath);
01108     }
01109 
01110     FindClose(hFile);
01111 }
01112 
01113 /* takes the null-separated file list and fills out the FILE_LIST */
01114 static HRESULT parse_file_list(FILE_LIST *flList, LPCWSTR szFiles)
01115 {
01116     LPCWSTR ptr = szFiles;
01117     WCHAR szCurFile[MAX_PATH];
01118     DWORD i = 0;
01119 
01120     if (!szFiles)
01121         return ERROR_INVALID_PARAMETER;
01122 
01123     flList->bAnyFromWildcard = FALSE;
01124     flList->bAnyDirectories = FALSE;
01125     flList->bAnyDontExist = FALSE;
01126     flList->num_alloc = 32;
01127     flList->dwNumFiles = 0;
01128 
01129     /* empty list */
01130     if (!szFiles[0])
01131         return ERROR_ACCESS_DENIED;
01132 
01133     flList->feFiles = (FILE_ENTRY *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
01134                                 flList->num_alloc * sizeof(FILE_ENTRY));
01135 
01136     while (*ptr)
01137     {
01138         if (i >= flList->num_alloc) grow_list( flList );
01139 
01140         /* change relative to absolute path */
01141         if (PathIsRelativeW(ptr))
01142         {
01143             GetCurrentDirectoryW(MAX_PATH, szCurFile);
01144             PathCombineW(szCurFile, szCurFile, ptr);
01145             flList->feFiles[i].bFromRelative = TRUE;
01146         }
01147         else
01148         {
01149             lstrcpyW(szCurFile, ptr);
01150             flList->feFiles[i].bFromRelative = FALSE;
01151         }
01152 
01153         /* parse wildcard files if they are in the filename */
01154         if (StrPBrkW(szCurFile, wWildcardChars))
01155         {
01156             parse_wildcard_files(flList, szCurFile, &i);
01157             flList->bAnyFromWildcard = TRUE;
01158             i--;
01159         }
01160         else
01161         {
01162             FILE_ENTRY *file = &flList->feFiles[i];
01163             add_file_to_entry(file, szCurFile);
01164             file->attributes = GetFileAttributesW( file->szFullPath );
01165             file->bExists = (file->attributes != INVALID_FILE_ATTRIBUTES);
01166 
01167             if (!file->bExists)
01168                 flList->bAnyDontExist = TRUE;
01169 
01170             if (IsAttribDir(file->attributes))
01171                 flList->bAnyDirectories = TRUE;
01172         }
01173 
01174         /* advance to the next string */
01175         ptr += lstrlenW(ptr) + 1;
01176         i++;
01177     }
01178     flList->dwNumFiles = i;
01179 
01180     return S_OK;
01181 }
01182 
01183 /* free the FILE_LIST */
01184 static void destroy_file_list(FILE_LIST *flList)
01185 {
01186     DWORD i;
01187 
01188     if (!flList || !flList->feFiles)
01189         return;
01190 
01191     for (i = 0; i < flList->dwNumFiles; i++)
01192     {
01193         HeapFree(GetProcessHeap(), 0, flList->feFiles[i].szDirectory);
01194         HeapFree(GetProcessHeap(), 0, flList->feFiles[i].szFilename);
01195         HeapFree(GetProcessHeap(), 0, flList->feFiles[i].szFullPath);
01196     }
01197 
01198     HeapFree(GetProcessHeap(), 0, flList->feFiles);
01199 }
01200 
01201 static void copy_dir_to_dir(FILE_OPERATION *op, const FILE_ENTRY *feFrom, LPCWSTR szDestPath)
01202 {
01203     WCHAR szFrom[MAX_PATH], szTo[MAX_PATH];
01204     SHFILEOPSTRUCTW fileOp;
01205 
01206     static const WCHAR wildCardFiles[] = {'*','.','*',0};
01207 
01208     if (IsDotDir(feFrom->szFilename))
01209         return;
01210 
01211     if (PathFileExistsW(szDestPath))
01212         PathCombineW(szTo, szDestPath, feFrom->szFilename);
01213     else
01214         lstrcpyW(szTo, szDestPath);
01215 
01216     if (!(op->req->fFlags & FOF_NOCONFIRMATION) && PathFileExistsW(szTo))
01217     {
01218         if (!SHELL_ConfirmDialogW(op->req->hwnd, ASK_OVERWRITE_FOLDER, feFrom->szFilename, op))
01219         {
01220             /* Vista returns an ERROR_CANCELLED even if user pressed "No" */
01221             if (!op->bManyItems)
01222                 op->bCancelled = TRUE;
01223             return;
01224         }
01225     }
01226 
01227     szTo[lstrlenW(szTo) + 1] = '\0';
01228     SHNotifyCreateDirectoryW(szTo, NULL);
01229 
01230     PathCombineW(szFrom, feFrom->szFullPath, wildCardFiles);
01231     szFrom[lstrlenW(szFrom) + 1] = '\0';
01232 
01233     fileOp = *op->req;
01234     fileOp.pFrom = szFrom;
01235     fileOp.pTo = szTo;
01236     fileOp.fFlags &= ~FOF_MULTIDESTFILES; /* we know we're copying to one dir */
01237 
01238     /* Don't ask the user about overwriting files when he accepted to overwrite the
01239        folder. FIXME: this is not exactly what Windows does - e.g. there would be
01240        an additional confirmation for a nested folder */
01241     fileOp.fFlags |= FOF_NOCONFIRMATION;
01242 
01243     SHFileOperationW(&fileOp);
01244 }
01245 
01246 static BOOL copy_file_to_file(FILE_OPERATION *op, const WCHAR *szFrom, const WCHAR *szTo)
01247 {
01248     if (!(op->req->fFlags & FOF_NOCONFIRMATION) && PathFileExistsW(szTo))
01249     {
01250         if (!SHELL_ConfirmDialogW(op->req->hwnd, ASK_OVERWRITE_FILE, PathFindFileNameW(szTo), op))
01251             return 0;
01252     }
01253 
01254     return SHNotifyCopyFileW(szFrom, szTo, FALSE) == 0;
01255 }
01256 
01257 /* copy a file or directory to another directory */
01258 static void copy_to_dir(FILE_OPERATION *op, const FILE_ENTRY *feFrom, const FILE_ENTRY *feTo)
01259 {
01260     if (!PathFileExistsW(feTo->szFullPath))
01261         SHNotifyCreateDirectoryW(feTo->szFullPath, NULL);
01262 
01263     if (IsAttribFile(feFrom->attributes))
01264     {
01265         WCHAR szDestPath[MAX_PATH];
01266 
01267         PathCombineW(szDestPath, feTo->szFullPath, feFrom->szFilename);
01268         copy_file_to_file(op, feFrom->szFullPath, szDestPath);
01269     }
01270     else if (!(op->req->fFlags & FOF_FILESONLY && feFrom->bFromWildcard))
01271         copy_dir_to_dir(op, feFrom, feTo->szFullPath);
01272 }
01273 
01274 static void create_dest_dirs(LPCWSTR szDestDir)
01275 {
01276     WCHAR dir[MAX_PATH];
01277     LPCWSTR ptr = StrChrW(szDestDir, '\\');
01278 
01279     /* make sure all directories up to last one are created */
01280     while (ptr && (ptr = StrChrW(ptr + 1, '\\')))
01281     {
01282         lstrcpynW(dir, szDestDir, ptr - szDestDir + 1);
01283 
01284         if (!PathFileExistsW(dir))
01285             SHNotifyCreateDirectoryW(dir, NULL);
01286     }
01287 
01288     /* create last directory */
01289     if (!PathFileExistsW(szDestDir))
01290         SHNotifyCreateDirectoryW(szDestDir, NULL);
01291 }
01292 
01293 /* the FO_COPY operation */
01294 static HRESULT copy_files(FILE_OPERATION *op, const FILE_LIST *flFrom, FILE_LIST *flTo)
01295 {
01296     DWORD i;
01297     const FILE_ENTRY *entryToCopy;
01298     const FILE_ENTRY *fileDest = &flTo->feFiles[0];
01299 
01300     if (flFrom->bAnyDontExist)
01301         return ERROR_SHELL_INTERNAL_FILE_NOT_FOUND;
01302 
01303     if (flTo->dwNumFiles == 0)
01304     {
01305         /* If the destination is empty, SHFileOperation should use the current directory */
01306         WCHAR curdir[MAX_PATH+1];
01307 
01308         GetCurrentDirectoryW(MAX_PATH, curdir);
01309         curdir[lstrlenW(curdir)+1] = 0;
01310 
01311         destroy_file_list(flTo);
01312         ZeroMemory(flTo, sizeof(FILE_LIST));
01313         parse_file_list(flTo, curdir);
01314         fileDest = &flTo->feFiles[0];
01315     }
01316 
01317     if (op->req->fFlags & FOF_MULTIDESTFILES)
01318     {
01319         if (flFrom->bAnyFromWildcard)
01320             return ERROR_CANCELLED;
01321 
01322         if (flFrom->dwNumFiles != flTo->dwNumFiles)
01323         {
01324             if (flFrom->dwNumFiles != 1 && !IsAttribDir(fileDest->attributes))
01325                 return ERROR_CANCELLED;
01326 
01327             /* Free all but the first entry. */
01328             for (i = 1; i < flTo->dwNumFiles; i++)
01329             {
01330                 HeapFree(GetProcessHeap(), 0, flTo->feFiles[i].szDirectory);
01331                 HeapFree(GetProcessHeap(), 0, flTo->feFiles[i].szFilename);
01332                 HeapFree(GetProcessHeap(), 0, flTo->feFiles[i].szFullPath);
01333             }
01334 
01335             flTo->dwNumFiles = 1;
01336         }
01337         else if (IsAttribDir(fileDest->attributes))
01338         {
01339             for (i = 1; i < flTo->dwNumFiles; i++)
01340                 if (!IsAttribDir(flTo->feFiles[i].attributes) ||
01341                     !IsAttribDir(flFrom->feFiles[i].attributes))
01342                 {
01343                     return ERROR_CANCELLED;
01344                 }
01345         }
01346     }
01347     else if (flFrom->dwNumFiles != 1)
01348     {
01349         if (flTo->dwNumFiles != 1 && !IsAttribDir(fileDest->attributes))
01350             return ERROR_CANCELLED;
01351 
01352         if (PathFileExistsW(fileDest->szFullPath) &&
01353             IsAttribFile(fileDest->attributes))
01354         {
01355             return ERROR_CANCELLED;
01356         }
01357 
01358         if (flTo->dwNumFiles == 1 && fileDest->bFromRelative &&
01359             !PathFileExistsW(fileDest->szFullPath))
01360         {
01361             return ERROR_CANCELLED;
01362         }
01363     }
01364 
01365     for (i = 0; i < flFrom->dwNumFiles; i++)
01366     {
01367         entryToCopy = &flFrom->feFiles[i];
01368 
01369         if ((op->req->fFlags & FOF_MULTIDESTFILES) &&
01370             flTo->dwNumFiles > 1)
01371         {
01372             fileDest = &flTo->feFiles[i];
01373         }
01374 
01375         if (IsAttribDir(entryToCopy->attributes) &&
01376             !lstrcmpiW(entryToCopy->szFullPath, fileDest->szDirectory))
01377         {
01378             return ERROR_SUCCESS;
01379         }
01380 
01381         create_dest_dirs(fileDest->szDirectory);
01382 
01383         if (!lstrcmpiW(entryToCopy->szFullPath, fileDest->szFullPath))
01384         {
01385             if (IsAttribFile(entryToCopy->attributes))
01386                 return ERROR_NO_MORE_SEARCH_HANDLES;
01387             else
01388                 return ERROR_SUCCESS;
01389         }
01390 
01391         if ((flFrom->dwNumFiles > 1 && flTo->dwNumFiles == 1) ||
01392             IsAttribDir(fileDest->attributes))
01393         {
01394             copy_to_dir(op, entryToCopy, fileDest);
01395         }
01396         else if (IsAttribDir(entryToCopy->attributes))
01397         {
01398             copy_dir_to_dir(op, entryToCopy, fileDest->szFullPath);
01399         }
01400         else
01401         {
01402             if (!copy_file_to_file(op, entryToCopy->szFullPath, fileDest->szFullPath))
01403             {
01404                 op->req->fAnyOperationsAborted = TRUE;
01405                 return ERROR_CANCELLED;
01406             }
01407         }
01408 
01409         /* Vista return code. XP would return e.g. ERROR_FILE_NOT_FOUND, ERROR_ALREADY_EXISTS */
01410         if (op->bCancelled)
01411             return ERROR_CANCELLED;
01412     }
01413 
01414     /* Vista return code. On XP if the used pressed "No" for the last item,
01415      * ERROR_ARENA_TRASHED would be returned */
01416     return ERROR_SUCCESS;
01417 }
01418 
01419 static BOOL confirm_delete_list(HWND hWnd, DWORD fFlags, BOOL fTrash, const FILE_LIST *flFrom)
01420 {
01421     if (flFrom->dwNumFiles > 1)
01422     {
01423         WCHAR tmp[8];
01424         const WCHAR format[] = {'%','d',0};
01425 
01426         wnsprintfW(tmp, sizeof(tmp)/sizeof(tmp[0]), format, flFrom->dwNumFiles);
01427         return SHELL_ConfirmDialogW(hWnd, (fTrash?ASK_TRASH_MULTIPLE_ITEM:ASK_DELETE_MULTIPLE_ITEM), tmp, NULL);
01428     }
01429     else
01430     {
01431         const FILE_ENTRY *fileEntry = &flFrom->feFiles[0];
01432 
01433         if (IsAttribFile(fileEntry->attributes))
01434             return SHELL_ConfirmDialogW(hWnd, (fTrash?ASK_TRASH_FILE:ASK_DELETE_FILE), fileEntry->szFullPath, NULL);
01435         else if (!(fFlags & FOF_FILESONLY && fileEntry->bFromWildcard))
01436             return SHELL_ConfirmDialogW(hWnd, (fTrash?ASK_TRASH_FOLDER:ASK_DELETE_FOLDER), fileEntry->szFullPath, NULL);
01437     }
01438     return TRUE;
01439 }
01440 
01441 /* the FO_DELETE operation */
01442 static HRESULT delete_files(LPSHFILEOPSTRUCTW lpFileOp, const FILE_LIST *flFrom)
01443 {
01444     const FILE_ENTRY *fileEntry;
01445     DWORD i;
01446     BOOL bPathExists;
01447     BOOL bTrash;
01448 
01449     if (!flFrom->dwNumFiles)
01450         return ERROR_SUCCESS;
01451 
01452     /* Windows also checks only the first item */
01453     bTrash = (lpFileOp->fFlags & FOF_ALLOWUNDO)
01454         && TRASH_CanTrashFile(flFrom->feFiles[0].szFullPath);
01455 
01456     if (!(lpFileOp->fFlags & FOF_NOCONFIRMATION) || (!bTrash && lpFileOp->fFlags & FOF_WANTNUKEWARNING))
01457         if (!confirm_delete_list(lpFileOp->hwnd, lpFileOp->fFlags, bTrash, flFrom))
01458         {
01459             lpFileOp->fAnyOperationsAborted = TRUE;
01460             return 0;
01461         }
01462 
01463     for (i = 0; i < flFrom->dwNumFiles; i++)
01464     {
01465         bPathExists = TRUE;
01466         fileEntry = &flFrom->feFiles[i];
01467 
01468         if (!IsAttribFile(fileEntry->attributes) &&
01469             (lpFileOp->fFlags & FOF_FILESONLY && fileEntry->bFromWildcard))
01470             continue;
01471 
01472         if (bTrash)
01473         {
01474             BOOL bDelete;
01475             if (TRASH_TrashFile(fileEntry->szFullPath))
01476                 continue;
01477 
01478             /* Note: Windows silently deletes the file in such a situation, we show a dialog */
01479             if (!(lpFileOp->fFlags & FOF_NOCONFIRMATION) || (lpFileOp->fFlags & FOF_WANTNUKEWARNING))
01480                 bDelete = SHELL_ConfirmDialogW(lpFileOp->hwnd, ASK_CANT_TRASH_ITEM, fileEntry->szFullPath, NULL);
01481             else
01482                 bDelete = TRUE;
01483 
01484             if (!bDelete)
01485             {
01486                 lpFileOp->fAnyOperationsAborted = TRUE;
01487                 break;
01488             }
01489         }
01490 
01491         /* delete the file or directory */
01492         if (IsAttribFile(fileEntry->attributes))
01493             bPathExists = DeleteFileW(fileEntry->szFullPath);
01494         else
01495             bPathExists = SHELL_DeleteDirectoryW(lpFileOp->hwnd, fileEntry->szFullPath, FALSE);
01496 
01497         if (!bPathExists)
01498         {
01499             DWORD err = GetLastError();
01500 
01501             if (ERROR_FILE_NOT_FOUND == err)
01502             {
01503                 // This is a windows 2003 server specific value which ahs been removed.
01504                 // Later versions of windows return ERROR_FILE_NOT_FOUND.
01505                 return 1026;
01506             }
01507             else
01508             {
01509                 return err;
01510             }
01511         }
01512     }
01513 
01514     return ERROR_SUCCESS;
01515 }
01516 
01517 static void move_dir_to_dir(LPSHFILEOPSTRUCTW lpFileOp, const FILE_ENTRY *feFrom, LPCWSTR szDestPath)
01518 {
01519     WCHAR szFrom[MAX_PATH], szTo[MAX_PATH];
01520     SHFILEOPSTRUCTW fileOp;
01521 
01522     static const WCHAR wildCardFiles[] = {'*','.','*',0};
01523 
01524     if (IsDotDir(feFrom->szFilename))
01525         return;
01526 
01527     SHNotifyCreateDirectoryW(szDestPath, NULL);
01528 
01529     PathCombineW(szFrom, feFrom->szFullPath, wildCardFiles);
01530     szFrom[lstrlenW(szFrom) + 1] = '\0';
01531 
01532     lstrcpyW(szTo, szDestPath);
01533     szTo[lstrlenW(szDestPath) + 1] = '\0';
01534 
01535     fileOp = *lpFileOp;
01536     fileOp.pFrom = szFrom;
01537     fileOp.pTo = szTo;
01538 
01539     SHFileOperationW(&fileOp);
01540 }
01541 
01542 /* moves a file or directory to another directory */
01543 static void move_to_dir(LPSHFILEOPSTRUCTW lpFileOp, const FILE_ENTRY *feFrom, const FILE_ENTRY *feTo)
01544 {
01545     WCHAR szDestPath[MAX_PATH];
01546 
01547     PathCombineW(szDestPath, feTo->szFullPath, feFrom->szFilename);
01548 
01549     if (IsAttribFile(feFrom->attributes))
01550         SHNotifyMoveFileW(feFrom->szFullPath, szDestPath);
01551     else if (!(lpFileOp->fFlags & FOF_FILESONLY && feFrom->bFromWildcard))
01552         move_dir_to_dir(lpFileOp, feFrom, szDestPath);
01553 }
01554 
01555 /* the FO_MOVE operation */
01556 static HRESULT move_files(LPSHFILEOPSTRUCTW lpFileOp, const FILE_LIST *flFrom, const FILE_LIST *flTo)
01557 {
01558     DWORD i;
01559     const FILE_ENTRY *entryToMove;
01560     const FILE_ENTRY *fileDest;
01561 
01562     if (!flFrom->dwNumFiles || !flTo->dwNumFiles)
01563         return ERROR_CANCELLED;
01564 
01565     if (!(lpFileOp->fFlags & FOF_MULTIDESTFILES) &&
01566         flTo->dwNumFiles > 1 && flFrom->dwNumFiles > 1)
01567     {
01568         return ERROR_CANCELLED;
01569     }
01570 
01571     if (!(lpFileOp->fFlags & FOF_MULTIDESTFILES) &&
01572         !flFrom->bAnyDirectories &&
01573         flFrom->dwNumFiles > flTo->dwNumFiles)
01574     {
01575         return ERROR_CANCELLED;
01576     }
01577 
01578     if (!PathFileExistsW(flTo->feFiles[0].szDirectory))
01579         return ERROR_CANCELLED;
01580 
01581     if ((lpFileOp->fFlags & FOF_MULTIDESTFILES) &&
01582         flFrom->dwNumFiles != flTo->dwNumFiles)
01583     {
01584         return ERROR_CANCELLED;
01585     }
01586 
01587     fileDest = &flTo->feFiles[0];
01588     for (i = 0; i < flFrom->dwNumFiles; i++)
01589     {
01590         entryToMove = &flFrom->feFiles[i];
01591 
01592         if (lpFileOp->fFlags & FOF_MULTIDESTFILES)
01593             fileDest = &flTo->feFiles[i];
01594 
01595         if (!PathFileExistsW(fileDest->szDirectory))
01596             return ERROR_CANCELLED;
01597 
01598         if (fileDest->bExists && IsAttribDir(fileDest->attributes))
01599             move_to_dir(lpFileOp, entryToMove, fileDest);
01600         else
01601             SHNotifyMoveFileW(entryToMove->szFullPath, fileDest->szFullPath);
01602     }
01603 
01604     return ERROR_SUCCESS;
01605 }
01606 
01607 /* the FO_RENAME files */
01608 static HRESULT rename_files(LPSHFILEOPSTRUCTW lpFileOp, const FILE_LIST *flFrom, const FILE_LIST *flTo)
01609 {
01610     const FILE_ENTRY *feFrom;
01611     const FILE_ENTRY *feTo;
01612 
01613     if (flFrom->dwNumFiles != 1)
01614         return ERROR_GEN_FAILURE;
01615 
01616     if (flTo->dwNumFiles != 1)
01617         return ERROR_CANCELLED;
01618 
01619     feFrom = &flFrom->feFiles[0];
01620     feTo= &flTo->feFiles[0];
01621 
01622     /* fail if destination doesn't exist */
01623     if (!feFrom->bExists)
01624         return ERROR_SHELL_INTERNAL_FILE_NOT_FOUND;
01625 
01626     /* fail if destination already exists */
01627     if (feTo->bExists)
01628         return ERROR_ALREADY_EXISTS;
01629 
01630     return SHNotifyMoveFileW(feFrom->szFullPath, feTo->szFullPath);
01631 }
01632 
01633 /* alert the user if an unsupported flag is used */
01634 static void check_flags(FILEOP_FLAGS fFlags)
01635 {
01636     WORD wUnsupportedFlags = FOF_NO_CONNECTED_ELEMENTS |
01637         FOF_NOCOPYSECURITYATTRIBS | FOF_NORECURSEREPARSE |
01638         FOF_RENAMEONCOLLISION | FOF_WANTMAPPINGHANDLE;
01639 
01640     if (fFlags & wUnsupportedFlags)
01641         FIXME("Unsupported flags: %04x\n", fFlags);
01642 }
01643 
01644 /*************************************************************************
01645  * SHFileOperationW          [SHELL32.@]
01646  *
01647  * See SHFileOperationA
01648  */
01649 int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp)
01650 {
01651     FILE_OPERATION op;
01652     FILE_LIST flFrom, flTo;
01653     int ret = 0;
01654 
01655     if (!lpFileOp)
01656         return ERROR_INVALID_PARAMETER;
01657 
01658     check_flags(lpFileOp->fFlags);
01659 
01660     ZeroMemory(&flFrom, sizeof(FILE_LIST));
01661     ZeroMemory(&flTo, sizeof(FILE_LIST));
01662 
01663     if ((ret = parse_file_list(&flFrom, lpFileOp->pFrom)))
01664         return ret;
01665 
01666     if (lpFileOp->wFunc != FO_DELETE)
01667         parse_file_list(&flTo, lpFileOp->pTo);
01668 
01669     ZeroMemory(&op, sizeof(op));
01670     op.req = lpFileOp;
01671     op.bManyItems = (flFrom.dwNumFiles > 1);
01672 
01673     switch (lpFileOp->wFunc)
01674     {
01675         case FO_COPY:
01676             ret = copy_files(&op, &flFrom, &flTo);
01677             break;
01678         case FO_DELETE:
01679             ret = delete_files(lpFileOp, &flFrom);
01680             break;
01681         case FO_MOVE:
01682             ret = move_files(lpFileOp, &flFrom, &flTo);
01683             break;
01684         case FO_RENAME:
01685             ret = rename_files(lpFileOp, &flFrom, &flTo);
01686             break;
01687         default:
01688             ret = ERROR_INVALID_PARAMETER;
01689             break;
01690     }
01691 
01692     destroy_file_list(&flFrom);
01693 
01694     if (lpFileOp->wFunc != FO_DELETE)
01695         destroy_file_list(&flTo);
01696 
01697     if (ret == ERROR_CANCELLED)
01698         lpFileOp->fAnyOperationsAborted = TRUE;
01699 
01700     return ret;
01701 }
01702 
01703 #define SHDSA_GetItemCount(hdsa) (*(int*)(hdsa))
01704 
01705 /*************************************************************************
01706  * SHFreeNameMappings      [shell32.246]
01707  *
01708  * Free the mapping handle returned by SHFileOperation if FOF_WANTSMAPPINGHANDLE
01709  * was specified.
01710  *
01711  * PARAMS
01712  *  hNameMapping [I] handle to the name mappings used during renaming of files
01713  *
01714  * RETURNS
01715  *  Nothing
01716  */
01717 void WINAPI SHFreeNameMappings(HANDLE hNameMapping)
01718 {
01719     if (hNameMapping)
01720     {
01721         int i = SHDSA_GetItemCount((HDSA)hNameMapping) - 1;
01722 
01723         for (; i>= 0; i--)
01724         {
01725             LPSHNAMEMAPPINGW lp = (SHNAMEMAPPINGW *)DSA_GetItemPtr((HDSA)hNameMapping, i);
01726 
01727             SHFree(lp->pszOldPath);
01728             SHFree(lp->pszNewPath);
01729         }
01730         DSA_Destroy((HDSA)hNameMapping);
01731     }
01732 }
01733 
01734 /*************************************************************************
01735  * SheGetDirA [SHELL32.@]
01736  *
01737  * drive = 0: returns the current directory path
01738  * drive > 0: returns the current directory path of the specified drive
01739  *            drive=1 -> A:  drive=2 -> B:  ...
01740  * returns 0 if successful
01741 */
01742 EXTERN_C DWORD WINAPI SheGetDirA(DWORD drive, LPSTR buffer)
01743 {
01744     WCHAR org_path[MAX_PATH];
01745     DWORD ret;
01746     char drv_path[3];
01747 
01748     /* change current directory to the specified drive */
01749     if (drive) {
01750         strcpy(drv_path, "A:");
01751         drv_path[0] += (char)drive-1;
01752 
01753         GetCurrentDirectoryW(MAX_PATH, org_path);
01754 
01755         SetCurrentDirectoryA(drv_path);
01756     }
01757 
01758     /* query current directory path of the specified drive */
01759     ret = GetCurrentDirectoryA(MAX_PATH, buffer);
01760 
01761     /* back to the original drive */
01762     if (drive)
01763         SetCurrentDirectoryW(org_path);
01764 
01765     if (!ret)
01766         return GetLastError();
01767 
01768     return 0;
01769 }
01770 
01771 /*************************************************************************
01772  * SheGetDirW [SHELL32.@]
01773  *
01774  * drive = 0: returns the current directory path
01775  * drive > 0: returns the current directory path of the specified drive
01776  *            drive=1 -> A:  drive=2 -> B:  ...
01777  * returns 0 if successful
01778  */
01779 EXTERN_C DWORD WINAPI SheGetDirW(DWORD drive, LPWSTR buffer)
01780 {
01781     WCHAR org_path[MAX_PATH];
01782     DWORD ret;
01783     char drv_path[3];
01784 
01785     /* change current directory to the specified drive */
01786     if (drive)
01787     {
01788         strcpy(drv_path, "A:");
01789         drv_path[0] += (char)drive-1;
01790 
01791         GetCurrentDirectoryW(MAX_PATH, org_path);
01792 
01793         SetCurrentDirectoryA(drv_path);
01794     }
01795 
01796     /* query current directory path of the specified drive */
01797     ret = GetCurrentDirectoryW(MAX_PATH, buffer);
01798 
01799     /* back to the original drive */
01800     if (drive)
01801         SetCurrentDirectoryW(org_path);
01802 
01803     if (!ret)
01804         return GetLastError();
01805 
01806     return 0;
01807 }
01808 
01809 /*************************************************************************
01810  * SheChangeDirA [SHELL32.@]
01811  *
01812  * changes the current directory to the specified path
01813  * and returns 0 if successful
01814  */
01815 EXTERN_C DWORD WINAPI SheChangeDirA(LPSTR path)
01816 {
01817     if (SetCurrentDirectoryA(path))
01818         return 0;
01819     else
01820         return GetLastError();
01821 }
01822 
01823 /*************************************************************************
01824  * SheChangeDirW [SHELL32.@]
01825  *
01826  * changes the current directory to the specified path
01827  * and returns 0 if successful
01828  */
01829 EXTERN_C DWORD WINAPI SheChangeDirW(LPWSTR path)
01830 {
01831     if (SetCurrentDirectoryW(path))
01832         return 0;
01833     else
01834         return GetLastError();
01835 }
01836 
01837 /*************************************************************************
01838  * IsNetDrive            [SHELL32.66]
01839  */
01840 EXTERN_C int WINAPI IsNetDrive(int drive)
01841 {
01842     char root[4];
01843     strcpy(root, "A:\\");
01844     root[0] += (char)drive;
01845     return (GetDriveTypeA(root) == DRIVE_REMOTE);
01846 }
01847 
01848 
01849 /*************************************************************************
01850  * RealDriveType                [SHELL32.524]
01851  */
01852 EXTERN_C INT WINAPI RealDriveType(INT drive, BOOL bQueryNet)
01853 {
01854     char root[] = "A:\\";
01855     root[0] += (char)drive;
01856     return GetDriveTypeA(root);
01857 }
01858 
01859 /***********************************************************************
01860  *              SHPathPrepareForWriteW (SHELL32.@)
01861  */
01862 EXTERN_C HRESULT WINAPI SHPathPrepareForWriteW(HWND hwnd, IUnknown *modless, LPCWSTR path, DWORD flags)
01863 {
01864     DWORD res;
01865     DWORD err;
01866     LPCWSTR realpath;
01867     int len;
01868     WCHAR* last_slash;
01869     WCHAR* temppath=NULL;
01870 
01871     TRACE("%p %p %s 0x%80x\n", hwnd, modless, debugstr_w(path), flags);
01872 
01873     if (flags & ~(SHPPFW_DIRCREATE|SHPPFW_ASKDIRCREATE|SHPPFW_IGNOREFILENAME))
01874         FIXME("unimplemented flags 0x%08x\n", flags);
01875 
01876     /* cut off filename if necessary */
01877     if (flags & SHPPFW_IGNOREFILENAME)
01878     {
01879         last_slash = StrRChrW(path, NULL, '\\');
01880         if (last_slash == NULL)
01881             len = 1;
01882         else
01883             len = last_slash - path + 1;
01884         temppath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
01885         if (!temppath)
01886             return E_OUTOFMEMORY;
01887         StrCpyNW(temppath, path, len);
01888         realpath = temppath;
01889     }
01890     else
01891     {
01892         realpath = path;
01893     }
01894 
01895     /* try to create the directory if asked to */
01896     if (flags & (SHPPFW_DIRCREATE|SHPPFW_ASKDIRCREATE))
01897     {
01898         if (flags & SHPPFW_ASKDIRCREATE)
01899             FIXME("treating SHPPFW_ASKDIRCREATE as SHPPFW_DIRCREATE\n");
01900 
01901         SHCreateDirectoryExW(0, realpath, NULL);
01902     }
01903 
01904     /* check if we can access the directory */
01905     res = GetFileAttributesW(realpath);
01906 
01907     HeapFree(GetProcessHeap(), 0, temppath);
01908 
01909     if (res == INVALID_FILE_ATTRIBUTES)
01910     {
01911         err = GetLastError();
01912         if (err == ERROR_FILE_NOT_FOUND)
01913             return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
01914         return HRESULT_FROM_WIN32(err);
01915     }
01916     else if (res & FILE_ATTRIBUTE_DIRECTORY)
01917         return S_OK;
01918     else
01919         return HRESULT_FROM_WIN32(ERROR_DIRECTORY);
01920 }
01921 
01922 /***********************************************************************
01923  *              SHPathPrepareForWriteA (SHELL32.@)
01924  */
01925 EXTERN_C HRESULT WINAPI SHPathPrepareForWriteA(HWND hwnd, IUnknown *modless, LPCSTR path, DWORD flags)
01926 {
01927     WCHAR wpath[MAX_PATH];
01928     MultiByteToWideChar( CP_ACP, 0, path, -1, wpath, MAX_PATH);
01929     return SHPathPrepareForWriteW(hwnd, modless, wpath, flags);
01930 }

Generated on Mon May 28 2012 04:26:06 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.