diff options
author | elfmz <fenix1905@tut.by> | 2021-12-31 00:27:16 +0300 |
---|---|---|
committer | elfmz <fenix1905@tut.by> | 2021-12-31 02:27:02 +0300 |
commit | a14dc1a81c797928d4f1b7d6a6b46ecc63f98308 (patch) | |
tree | c27c61ac33582bc4d469c6608cd042add388f230 /far2l/src/copy.cpp | |
parent | d5f1bf245e96834d44390d1723cfef3dfbb1fb02 (diff) |
shuffle a bit far2l sources
Diffstat (limited to 'far2l/src/copy.cpp')
-rw-r--r-- | far2l/src/copy.cpp | 3510 |
1 files changed, 3510 insertions, 0 deletions
diff --git a/far2l/src/copy.cpp b/far2l/src/copy.cpp new file mode 100644 index 00000000..7cf532db --- /dev/null +++ b/far2l/src/copy.cpp @@ -0,0 +1,3510 @@ +/* +copy.cpp + +Копирование файлов +*/ +/* +Copyright (c) 1996 Eugene Roshal +Copyright (c) 2000 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "headers.hpp" + + +#include "copy.hpp" +#include "lang.hpp" +#include "keys.hpp" +#include "colors.hpp" +#include "dialog.hpp" +#include "ctrlobj.hpp" +#include "filepanels.hpp" +#include "panel.hpp" +#include "filelist.hpp" +#include "foldtree.hpp" +#include "treelist.hpp" +#include "chgprior.hpp" +#include "scantree.hpp" +#include "constitle.hpp" +#include "filefilter.hpp" +#include "fileview.hpp" +#include "TPreRedrawFunc.hpp" +#include "syslog.hpp" +#include "cddrv.hpp" +#include "interf.hpp" +#include "keyboard.hpp" +#include "palette.hpp" +#include "message.hpp" +#include "config.hpp" +#include "stddlg.hpp" +#include "fileattr.hpp" +#include "datetime.hpp" +#include "dirinfo.hpp" +#include "pathmix.hpp" +#include "drivemix.hpp" +#include "dirmix.hpp" +#include "strmix.hpp" +#include "panelmix.hpp" +#include "processname.hpp" +#include "mix.hpp" +#include "DlgGuid.hpp" +#include "console.hpp" +#include "wakeful.hpp" +#include <unistd.h> + +#if defined(__APPLE__) +# include <AvailabilityMacros.h> +# if defined(MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 +# include <sys/attr.h> +# include <sys/clonefile.h> +# define COW_SUPPORTED +# endif + +#elif defined(__linux__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 27)) +# define COW_SUPPORTED + +#endif + +/* Общее время ожидания пользователя */ +extern long WaitUserTime; +/* Для того, что бы время при ожидании пользователя тикало, а remaining/speed нет */ +static long OldCalcTime; + +#define PROGRESS_REFRESH_THRESHOLD 500 // msec + +enum {COPY_BUFFER_SIZE = 0x800000, COPY_PIECE_MINIMAL = 0x10000}; + +enum +{ + COPY_RULE_NUL = 0x0001, + COPY_RULE_FILES = 0x0002, +}; + +static int TotalFiles,TotalFilesToProcess; + +static clock_t CopyStartTime; + +static int OrigScrX,OrigScrY; + +static uint64_t TotalCopySize, TotalCopiedSize; // Общий индикатор копирования +static uint64_t CurCopiedSize; // Текущий индикатор копирования +static uint64_t TotalSkippedSize; // Общий размер пропущенных файлов +static size_t CountTarget; // всего целей. +static bool ShowTotalCopySize; +static FARString strTotalCopySizeText; + +static FileFilter *Filter; +static int UseFilter=FALSE; + +static BOOL ZoomedState,IconicState; +static clock_t ProgressUpdateTime; // Last progress bar update time + +ShellCopyFileExtendedAttributes::ShellCopyFileExtendedAttributes(File &f) +{ + _apply = (f.QueryFileExtendedAttributes(_xattr) != FB_NO && !_xattr.empty()); +} + +void ShellCopyFileExtendedAttributes::ApplyToCopied(File &f) +{ + if (_apply) { + f.SetFileExtendedAttributes(_xattr); + } +} + +struct CopyDlgParam +{ + ShellCopy *thisClass; + int AltF10; + DWORD FileAttr; + int SelCount; + bool FolderPresent; + bool FilesPresent; + FARString strPluginFormat; + bool AskRO; +}; + +enum enumShellCopy +{ + ID_SC_TITLE, + ID_SC_TARGETTITLE, + ID_SC_TARGETEDIT, + ID_SC_SEPARATOR1, + ID_SC_COMBOTEXT, + ID_SC_COMBO, + ID_SC_MULTITARGET, + ID_SC_COPYACCESSMODE, + ID_SC_COPYXATTR, + ID_SC_WRITETHROUGH, + ID_SC_SPARSEFILES, + ID_SC_USECOW, + ID_SC_COPYSYMLINK_TEXT, + ID_SC_COPYSYMLINK_COMBO, + ID_SC_SEPARATOR3, + ID_SC_USEFILTER, + ID_SC_SEPARATOR4, + ID_SC_BTNCOPY, + ID_SC_BTNTREE, + ID_SC_BTNFILTER, + ID_SC_BTNCANCEL, + ID_SC_SOURCEFILENAME, +}; + +enum CopyMode +{ + CM_ASK, + CM_OVERWRITE, + CM_SKIP, + CM_RENAME, + CM_APPEND, + CM_ONLYNEWER, + CM_SEPARATOR, + CM_ASKRO, +}; + +// CopyProgress start +// гнать это отсюда в отдельный файл после разбора кучи глобальных переменных вверху +class CopyProgress +{ + ConsoleTitle CopyTitle; + wakeful W; + SMALL_RECT Rect; + wchar_t Bar[100]; + size_t BarSize; + bool Move,Total,Time; + bool BgInit,ScanBgInit; + bool IsCancelled; + int Color; + int Percents; + DWORD LastWriteTime; + FARString strSrc,strDst,strFiles,strTime; + bool Timer(); + void Flush(); + void DrawNames(); + void CreateScanBackground(); + void SetProgress(bool TotalProgress,UINT64 CompletedSize,UINT64 TotalSize); + public: + CopyProgress(bool Move,bool Total,bool Time); + void CreateBackground(); + bool Cancelled() {return IsCancelled;}; + void SetScanName(const wchar_t *Name); + void SetNames(const wchar_t *Src,const wchar_t *Dst); + void SetProgressValue(UINT64 CompletedSize,UINT64 TotalSize) {return SetProgress(false,CompletedSize,TotalSize);} + void SetTotalProgressValue(UINT64 CompletedSize,UINT64 TotalSize) {return SetProgress(true,CompletedSize,TotalSize);} +}; + +static void GetTimeText(DWORD Time,FARString &strTimeText) +{ + DWORD Sec=Time; + DWORD Min=Sec/60; + Sec-=(Min*60); + DWORD Hour=Min/60; + Min-=(Hour*60); + strTimeText.Format(L"%02u:%02u:%02u",Hour,Min,Sec); +} + +bool CopyProgress::Timer() +{ + bool Result=false; + DWORD Time=GetProcessUptimeMSec(); + + if (!LastWriteTime||(Time-LastWriteTime>=RedrawTimeout)) + { + LastWriteTime=Time; + Result=true; + } + + return Result; +} + +void CopyProgress::Flush() +{ + if (Timer()) + { + if (!IsCancelled) + { + if (CheckForEscSilent()) + { + (*FrameManager)[0]->Lock(); + IsCancelled=ConfirmAbortOp()!=0; + (*FrameManager)[0]->Unlock(); + } + } + + if (Total || (TotalFilesToProcess==1)) + { + CopyTitle.Set(L"{%d%%} %ls",Total?ToPercent64(TotalCopiedSize>>8,TotalCopySize>>8):Percents,Move?MSG(MCopyMovingTitle):MSG(MCopyCopyingTitle)); + } + } +} + +CopyProgress::CopyProgress(bool Move,bool Total,bool Time): + BarSize(52), + Move(Move), + Total(Total), + Time(Time), + BgInit(false), + ScanBgInit(false), + IsCancelled(false), + Color(FarColorToReal(COL_DIALOGTEXT)), + Percents(0), + LastWriteTime(0) +{ +} + +void CopyProgress::SetScanName(const wchar_t *Name) +{ + if (!ScanBgInit) + { + CreateScanBackground(); + } + + GotoXY(Rect.Left+5,Rect.Top+3); + FS<<fmt::LeftAlign()<<fmt::Width(Rect.Right-Rect.Left-9)<<fmt::Precision(Rect.Right-Rect.Left-9)<<Name; + Flush(); +} + +void CopyProgress::CreateScanBackground() +{ + for (size_t i=0; i<BarSize; i++) + { + Bar[i]=L' '; + } + + Bar[BarSize]=0; + Message(MSG_LEFTALIGN,0,MSG(Move?MMoveDlgTitle:MCopyDlgTitle),MSG(MCopyScanning),Bar); + int MX1,MY1,MX2,MY2; + GetMessagePosition(MX1,MY1,MX2,MY2); + Rect.Left=MX1; + Rect.Right=MX2; + Rect.Top=MY1; + Rect.Bottom=MY2; + ScanBgInit=true; +} + +void CopyProgress::CreateBackground() +{ + for (size_t i=0; i<BarSize; i++) + { + Bar[i]=L' '; + } + + Bar[BarSize]=0; + + if (!Total) + { + if (!Time) + { + Message(MSG_LEFTALIGN,0,MSG(Move?MMoveDlgTitle:MCopyDlgTitle),MSG(Move?MCopyMoving:MCopyCopying),L"",MSG(MCopyTo),L"",Bar,L"\x1",L""); + } + else + { + Message(MSG_LEFTALIGN,0,MSG(Move?MMoveDlgTitle:MCopyDlgTitle),MSG(Move?MCopyMoving:MCopyCopying),L"",MSG(MCopyTo),L"",Bar,L"\x1",L"",L"\x1",L""); + } + } + else + { + FARString strTotalSeparator(L"\x1 "); + strTotalSeparator+=MSG(MCopyDlgTotal); + strTotalSeparator+=L": "; + strTotalSeparator+=strTotalCopySizeText; + strTotalSeparator+=L" "; + + if (!Time) + { + Message(MSG_LEFTALIGN,0,MSG(Move?MMoveDlgTitle:MCopyDlgTitle),MSG(Move?MCopyMoving:MCopyCopying),L"",MSG(MCopyTo),L"",Bar,strTotalSeparator,Bar,L"\x1",L""); + } + else + { + Message(MSG_LEFTALIGN,0,MSG(Move?MMoveDlgTitle:MCopyDlgTitle),MSG(Move?MCopyMoving:MCopyCopying),L"",MSG(MCopyTo),L"",Bar,strTotalSeparator,Bar,L"\x1",L"",L"\x1",L""); + } + } + + int MX1,MY1,MX2,MY2; + GetMessagePosition(MX1,MY1,MX2,MY2); + Rect.Left=MX1; + Rect.Right=MX2; + Rect.Top=MY1; + Rect.Bottom=MY2; + BgInit=true; + DrawNames(); +} + +void CopyProgress::DrawNames() +{ + Text(Rect.Left+5,Rect.Top+3,Color,strSrc); + Text(Rect.Left+5,Rect.Top+5,Color,strDst); + Text(Rect.Left+5,Rect.Top+(Total?10:8),Color,strFiles); +} + +void CopyProgress::SetNames(const wchar_t *Src,const wchar_t *Dst) +{ + if (!BgInit) + { + CreateBackground(); + } + + FormatString FString; + FString<<fmt::LeftAlign()<<fmt::Width(Rect.Right-Rect.Left-9)<<fmt::Precision(Rect.Right-Rect.Left-9)<<Src; + strSrc=std::move(FString.strValue()); + FString.Clear(); + FString<<fmt::LeftAlign()<<fmt::Width(Rect.Right-Rect.Left-9)<<fmt::Precision(Rect.Right-Rect.Left-9)<<Dst; + strDst=std::move(FString.strValue()); + + if (Total) + { + strFiles.Format(MSG(MCopyProcessedTotal),TotalFiles,TotalFilesToProcess); + } + else + { + strFiles.Format(MSG(MCopyProcessed),TotalFiles); + } + + DrawNames(); + Flush(); +} + +void CopyProgress::SetProgress(bool TotalProgress,UINT64 CompletedSize,UINT64 TotalSize) +{ + if (!BgInit) + { + CreateBackground(); + } + + if (Total==TotalProgress) + { + } + + UINT64 OldCompletedSize = CompletedSize; + UINT64 OldTotalSize = TotalSize; + CompletedSize>>=8; + TotalSize>>=8; + CompletedSize=Min(CompletedSize,TotalSize); + COORD BarCoord={(SHORT)(Rect.Left+5),(SHORT)(Rect.Top+(TotalProgress?8:6))}; + size_t BarLength=Rect.Right-Rect.Left-9-5; //-5 для процентов + size_t Length=TotalSize?static_cast<size_t>((TotalSize<1000000?CompletedSize:CompletedSize/100)*BarLength/(TotalSize<1000000?TotalSize:TotalSize/100)):BarLength; + + for (size_t i=0; i<BarLength; i++) + { + Bar[i]=BoxSymbols[BS_X_B0]; + } + + if (TotalSize) + { + for (size_t i=0; i<Length; i++) + { + Bar[i]=BoxSymbols[BS_X_DB]; + } + } + + Bar[BarLength]=0; + Percents=ToPercent64(CompletedSize,TotalSize); + FormatString strPercents; + strPercents<<fmt::Width(4)<<Percents<<L"%"; + Text(BarCoord.X,BarCoord.Y,Color,Bar); + Text(static_cast<int>(BarCoord.X+BarLength),BarCoord.Y,Color,strPercents); + + if (Time&&(!Total||TotalProgress)) + { + DWORD WorkTime=GetProcessUptimeMSec()-CopyStartTime; + UINT64 SizeLeft=(OldTotalSize>OldCompletedSize)?(OldTotalSize-OldCompletedSize):0; + long CalcTime=OldCalcTime; + + if (WaitUserTime!=-1) // -1 => находимся в процессе ожидания ответа юзера + { + OldCalcTime=CalcTime=WorkTime-WaitUserTime; + } + + WorkTime/=1000; + CalcTime/=1000; + + if (!WorkTime) + { + strTime.Format(MSG(MCopyTimeInfo),L" ",L" ",L" "); + } + else + { + if (TotalProgress) + { + OldCompletedSize=OldCompletedSize-TotalSkippedSize; + } + + UINT64 CPS=CalcTime?OldCompletedSize/CalcTime:0; + DWORD TimeLeft=static_cast<DWORD>(CPS?SizeLeft/CPS:0); + FARString strSpeed; + FileSizeToStr(strSpeed,CPS,8,COLUMN_FLOATSIZE|COLUMN_COMMAS); + FARString strWorkTimeStr,strTimeLeftStr; + GetTimeText(WorkTime,strWorkTimeStr); + GetTimeText(TimeLeft,strTimeLeftStr); + if(strSpeed.At(0)==L' ' && strSpeed.At(strSpeed.GetLength()-1)>=L'0' && strSpeed.At(strSpeed.GetLength()-1)<=L'9') + { + strSpeed.LShift(1); + strSpeed+=L" "; + } + strTime.Format(MSG(MCopyTimeInfo),strWorkTimeStr.CPtr(),strTimeLeftStr.CPtr(),strSpeed.CPtr()); + } + + Text(Rect.Left+5,Rect.Top+(Total?12:10),Color,strTime); + } + + Flush(); +} +// CopyProgress end + +CopyProgress *CP; + + +/* $ 25.05.2002 IS + + Всегда работаем с реальными _длинными_ именами, в результате чего + отлавливается ситуация, когда + Src="D:\Program Files\filename" + Dest="D:\PROGRA~1\filename" + ("D:\PROGRA~1" - короткое имя для "D:\Program Files") + считается, что имена тоже одинаковые, а раньше считалось, + что они разные (функция не знала, что и в первом, и во втором случае + путь один и тот же) + ! Оптимизация - "велосипед" заменен на DeleteEndSlash + ! Убираем всю самодеятельность по проверке имен с разным + регистром из функции прочь, потому что это нужно делать только при + переименовании, а функция вызывается и при копировании тоже. + Теперь функция вернет 1, для случая имен src=path\filename, + dest=path\filename (раньше возвращала 2 - т.е. сигнал об ошибке). +*/ + +static int CmpFullNames(const wchar_t *Src,const wchar_t *Dest) +{ + FARString strSrcFullName, strDestFullName; + + // получим полные пути с учетом символических связей + ConvertNameToFull(Src, strSrcFullName); + ConvertNameToFull(Dest, strDestFullName); + DeleteEndSlash(strSrcFullName); + DeleteEndSlash(strDestFullName); + + return !StrCmp(strSrcFullName,strDestFullName); +} + +static FARString& GetParentFolder(const wchar_t *Src, FARString &strDest) +{ + strDest = Src; + CutToSlash(strDest,true); + return strDest; +} + +static int CmpFullPath(const wchar_t *Src, const wchar_t *Dest) +{ + FARString strSrcFullName, strDestFullName; + + GetParentFolder(Src, strSrcFullName); + GetParentFolder(Dest, strDestFullName); + DeleteEndSlash(strSrcFullName); + DeleteEndSlash(strDestFullName); + + return !StrCmp(strSrcFullName, strDestFullName); +} + +static void GenerateName(FARString &strName,const wchar_t *Path=nullptr) +{ + if (Path&&*Path) + { + FARString strTmp=Path; + AddEndSlash(strTmp); + strTmp+=PointToName(strName); + strName=strTmp; + } + + FARString strExt=PointToExt(strName); + size_t NameLength=strName.GetLength()-strExt.GetLength(); + + for (int i=1; apiGetFileAttributes(strName)!=INVALID_FILE_ATTRIBUTES; i++) + { + WCHAR Suffix[20]=L"_"; + _itow(i,Suffix+1,10); + strName.Truncate(NameLength); + strName+=Suffix; + strName+=strExt; + } +} + +void PR_ShellCopyMsg() +{ + PreRedrawItem preRedrawItem=PreRedraw.Peek(); + + if (preRedrawItem.Param.Param1) + { + ((CopyProgress*)preRedrawItem.Param.Param1)->CreateBackground(); + } +} + +BOOL CheckAndUpdateConsole(BOOL IsChangeConsole) +{ + BOOL curZoomedState = Console.IsZoomed(); + BOOL curIconicState = Console.IsIconic(); + + if (ZoomedState!=curZoomedState && IconicState==curIconicState) + { + ZoomedState=curZoomedState; + ChangeVideoMode(ZoomedState); + Frame *frame=FrameManager->GetBottomFrame(); + int LockCount=-1; + + while (frame->Locked()) + { + LockCount++; + frame->Unlock(); + } + + FrameManager->ResizeAllFrame(); + FrameManager->PluginCommit(); + + while (LockCount > 0) + { + frame->Lock(); + LockCount--; + } + + IsChangeConsole=TRUE; + } + + return IsChangeConsole; +} + +#define USE_PAGE_SIZE 0x1000 +template <class T> static T AlignPageUp(T v) +{ +//todo: use actual system page size + uintptr_t al = ((uintptr_t)v) & (USE_PAGE_SIZE - 1); + if (al) { + v = (T)(((uintptr_t)v) + (USE_PAGE_SIZE - al)); + } + return v; +} + +ShellCopyBuffer::ShellCopyBuffer() + : + Capacity(AlignPageUp((DWORD)COPY_BUFFER_SIZE)), + Size(std::min((DWORD)COPY_PIECE_MINIMAL, Capacity)), + // allocate page-aligned memory: IO works faster on that, also direct-io requires buffer to be aligned sometimes + // OSX lacks aligned_malloc so do it manually + Buffer(new char[Capacity + USE_PAGE_SIZE]), + Ptr(AlignPageUp(Buffer)) +{ +} + +ShellCopyBuffer::~ShellCopyBuffer() +{ + delete[] Buffer; +} + +ShellCopy::ShellCopy(Panel *SrcPanel, // исходная панель (активная) + int Move, // =1 - операция Move + int Link, // =1 - Sym/Hard Link + int CurrentOnly, // =1 - только текущий файл, под курсором + int Ask, // =1 - выводить диалог? + int &ToPlugin, // =? + const wchar_t *PluginDestPath, + bool ToSubdir): + RPT(RP_EXACTCOPY) +{ + Filter=nullptr; + DestList.SetParameters(0,0,ULF_UNIQUE); + CopyDlgParam CDP{}; + if (!(CDP.SelCount=SrcPanel->GetSelCount())) + return; + + FARString strSelName; + + if (CDP.SelCount==1) + { + SrcPanel->GetSelNameCompat(nullptr,CDP.FileAttr); //???? + SrcPanel->GetSelNameCompat(&strSelName,CDP.FileAttr); + + if (TestParentFolderName(strSelName)) + return; + } + + ZoomedState=Console.IsZoomed(); + IconicState=Console.IsIconic(); + // Создадим объект фильтра + Filter=new FileFilter(SrcPanel, FFT_COPY); + // $ 26.05.2001 OT Запретить перерисовку панелей во время копирования + _tran(SysLog(L"call (*FrameManager)[0]->LockRefresh()")); + (*FrameManager)[0]->Lock(); + + // Progress bar update threshold + CDP.thisClass=this; + CDP.AltF10=0; + CDP.FolderPresent=false; + CDP.FilesPresent=false; + if (Move) Flags.MOVE = true; + if (Link) Flags.LINK = true; + if (CurrentOnly) Flags.CURRENTONLY = true; + ShowTotalCopySize=Opt.CMOpt.CopyShowTotal!=0; + strTotalCopySizeText.Clear(); + SelectedFolderNameLength=0; + int DestPlugin=ToPlugin; + ToPlugin=FALSE; + this->SrcPanel=SrcPanel; + DestPanel=CtrlObject->Cp()->GetAnotherPanel(SrcPanel); + DestPanelMode=DestPlugin ? DestPanel->GetMode():NORMAL_PANEL; + SrcPanelMode=SrcPanel->GetMode(); + + // *********************************************************************** + // *** Prepare Dialog Controls + // *********************************************************************** + int DLG_HEIGHT=19, DLG_WIDTH=76; + + DialogDataEx CopyDlgData[]= + { + {DI_DOUBLEBOX, 3, 1,(SHORT)(DLG_WIDTH-4),(SHORT)(DLG_HEIGHT-2),{},0,MSG(MCopyDlgTitle)}, + {DI_TEXT, 5, 2, 0, 2,{},0,MSG(Link?MCMLTargetIN:MCMLTargetTO)}, + {DI_EDIT, 5, 3,70, 3,{reinterpret_cast<DWORD_PTR>(L"Copy")},DIF_FOCUS|DIF_HISTORY|DIF_EDITEXPAND|DIF_USELASTHISTORY|DIF_EDITPATH,L""}, + {DI_TEXT, 3, 4, 0, 4,{},DIF_SEPARATOR,L""}, + {DI_TEXT, 5, 5, 0, 5,{},0,MSG(MCopyIfFileExist)}, + {DI_COMBOBOX, 29, 5,70, 5,{},DIF_DROPDOWNLIST|DIF_LISTNOAMPERSAND|DIF_LISTWRAPMODE,L""}, + {DI_CHECKBOX, 5, 6, 0, 6,{},0,MSG(MCopyMultiActions)}, + {DI_CHECKBOX, 5, 7, 0, 7,{},0,MSG(MCopyAccessMode)}, + {DI_CHECKBOX, 5, 8, 0, 8,{},0,MSG(MCopyXAttr)}, + {DI_CHECKBOX, 5, 9, 0, 9,{},0,MSG(MCopyWriteThrough)}, + {DI_CHECKBOX, 5, 10, 0, 10,{},0,MSG(MCopySparseFiles)}, + {DI_CHECKBOX, 5, 11, 0, 11,{},0,MSG(MCopyUseCOW)}, + {DI_TEXT, 5, 12, 0, 12,{},0,MSG(MCopySymLinkText)}, + {DI_COMBOBOX, 29, 12,70, 12,{},DIF_DROPDOWNLIST|DIF_LISTNOAMPERSAND|DIF_LISTWRAPMODE,L""}, + {DI_TEXT, 3,13, 0,13,{},DIF_SEPARATOR,L""}, + {DI_CHECKBOX, 5,14, 0,14,{UseFilter?BSTATE_CHECKED:BSTATE_UNCHECKED},DIF_AUTOMATION,(wchar_t *)MCopyUseFilter}, + {DI_TEXT, 3,15, 0,15,{},DIF_SEPARATOR,L""}, + {DI_BUTTON, 0,16, 0,16,{},DIF_DEFAULT|DIF_CENTERGROUP,MSG(MCopyDlgCopy)}, + {DI_BUTTON, 0,16, 0,16,{},DIF_CENTERGROUP|DIF_BTNNOCLOSE,MSG(MCopyDlgTree)}, + {DI_BUTTON, 0,16, 0,16,{},DIF_CENTERGROUP|DIF_BTNNOCLOSE|DIF_AUTOMATION|(UseFilter?0:DIF_DISABLE),MSG(MCopySetFilter)}, + {DI_BUTTON, 0,16, 0,16,{},DIF_CENTERGROUP,MSG(MCopyDlgCancel)}, + {DI_TEXT, 5, 2, 0, 2,{},DIF_SHOWAMPERSAND,L""} + }; + MakeDialogItemsEx(CopyDlgData,CopyDlg); + CopyDlg[ID_SC_MULTITARGET].Selected=Opt.CMOpt.MultiCopy; + CopyDlg[ID_SC_WRITETHROUGH].Selected=Opt.CMOpt.WriteThrough; + CopyDlg[ID_SC_SPARSEFILES].Selected=Opt.CMOpt.SparseFiles; +#ifdef COW_SUPPORTED + CopyDlg[ID_SC_USECOW].Selected=Opt.CMOpt.UseCOW && (Opt.CMOpt.SparseFiles == 0); +#else + CopyDlg[ID_SC_USECOW].Flags|= DIF_DISABLE | DIF_HIDDEN; +#endif + CopyDlg[ID_SC_COPYACCESSMODE].Selected=Opt.CMOpt.CopyAccessMode; + CopyDlg[ID_SC_COPYXATTR].Selected=Opt.CMOpt.CopyXAttr; + + FarList SymLinkHowComboList; + FarListItem SymLinkHowTypeItems[3] {}; + + if (Link) + { + CopyDlg[ID_SC_COMBOTEXT].strData = MSG(MLinkType); +// CopyDlg[ID_SC_SEPARATOR2].Flags|= DIF_DISABLE|DIF_HIDDEN; + CopyDlg[ID_SC_COPYSYMLINK_TEXT].Flags|= DIF_DISABLE|DIF_HIDDEN; + CopyDlg[ID_SC_COPYSYMLINK_COMBO].Flags|= DIF_DISABLE|DIF_HIDDEN; + CopyDlg[ID_SC_SPARSEFILES].Flags|= DIF_DISABLE|DIF_HIDDEN; + CopyDlg[ID_SC_USECOW].Flags|= DIF_DISABLE|DIF_HIDDEN; + + } + else + { + SymLinkHowTypeItems[0].Text = MSG(MLinkCopyAsIs); + SymLinkHowTypeItems[1].Text = MSG(MLinkCopySmart); + SymLinkHowTypeItems[2].Text = MSG(MLinkCopyContent); + SymLinkHowComboList.ItemsNumber = ARRAYSIZE(SymLinkHowTypeItems); + SymLinkHowComboList.Items = SymLinkHowTypeItems; + SymLinkHowTypeItems[std::min(Opt.CMOpt.HowCopySymlink, (int)ARRAYSIZE(SymLinkHowTypeItems) - 1)].Flags|= LIF_SELECTED; + + CopyDlg[ID_SC_COPYSYMLINK_COMBO].ListItems = &SymLinkHowComboList; + + if (Move) // секция про перенос + { + CopyDlg[ID_SC_MULTITARGET].Selected = 0; + CopyDlg[ID_SC_MULTITARGET].Flags |= DIF_DISABLE; + } +// else // секция про копирование +// { +// CopyDlg[ID_SC_COPYSYMLINKOUTER].Selected=1; +// } + } + + FARString strCopyStr; + + if (CDP.SelCount==1) + { + if (SrcPanel->GetType()==TREE_PANEL) + { + FARString strNewDir(strSelName); + size_t pos; + + if (FindLastSlash(pos,strNewDir)) + { + strNewDir.Truncate(pos); + + if (!pos || strNewDir.At(pos-1)==L':') + strNewDir += L"/"; + + FarChDir(strNewDir); + } + } + + FARString strSelNameShort = strSelName; + strCopyStr=MSG(Move?MMoveFile:(Link?MLinkFile:MCopyFile)); + TruncPathStr(strSelNameShort,static_cast<int>(CopyDlg[ID_SC_TITLE].X2-CopyDlg[ID_SC_TITLE].X1-strCopyStr.GetLength()-7)); + strCopyStr+=L" "+strSelNameShort; + + // Если копируем одиночный файл, то запрещаем использовать фильтр + if (!(CDP.FileAttr&FILE_ATTRIBUTE_DIRECTORY)) + { + CopyDlg[ID_SC_USEFILTER].Selected=0; + CopyDlg[ID_SC_USEFILTER].Flags|=DIF_DISABLE; + } + } + else // Объектов несколько! + { + int NOper=MCopyFiles; + + if (Move) NOper=MMoveFiles; + else if (Link) NOper=MLinkFiles; + + // коррекция языка - про окончания + FormatString StrItems; + StrItems<<CDP.SelCount; + size_t LenItems=StrItems.strValue().GetLength(); + int NItems=MCMLItemsA; + + if (LenItems > 0) + { + if ((LenItems >= 2 && StrItems[LenItems-2] == '1') || + StrItems[LenItems-1] >= '5' || + StrItems[LenItems-1] == '0') + NItems=MCMLItemsS; + else if (StrItems[LenItems-1] == '1') + NItems=MCMLItems0; + } + + strCopyStr.Format(MSG(NOper),CDP.SelCount,MSG(NItems)); + } + + CopyDlg[ID_SC_SOURCEFILENAME].strData=strCopyStr; + CopyDlg[ID_SC_TITLE].strData = MSG(Move?MMoveDlgTitle :(Link?MLinkDlgTitle:MCopyDlgTitle)); + CopyDlg[ID_SC_BTNCOPY].strData = MSG(Move?MCopyDlgRename:(Link?MCopyDlgLink:MCopyDlgCopy)); + + if (DestPanelMode == PLUGIN_PANEL) + { + // Если противоположная панель - плагин, то дисаблим OnlyNewer //????? +/* + CDP.CopySecurity=2; + CopyDlg[ID_SC_ACCOPY].Selected=0; + CopyDlg[ID_SC_ACINHERIT].Selected=0; + CopyDlg[ID_SC_ACLEAVE].Selected=1; + CopyDlg[ID_SC_ACCOPY].Flags|=DIF_DISABLE; + CopyDlg[ID_SC_ACINHERIT].Flags|=DIF_DISABLE; + CopyDlg[ID_SC_ACLEAVE].Flags|=DIF_DISABLE; +*/ + } + + FARString strDestDir; + DestPanel->GetCurDir(strDestDir); + if(ToSubdir) + { + AddEndSlash(strDestDir); + FARString strSubdir; + DestPanel->GetCurName(strSubdir); + strDestDir+=strSubdir; + } + FARString strSrcDir; + SrcPanel->GetCurDir(strSrcDir); + + if (CurrentOnly) + { + // При копировании только элемента под курсором берем его имя в кавычки, если оно содержит разделители. + CopyDlg[ID_SC_TARGETEDIT].strData = strSelName; + + if (!Move && CopyDlg[ID_SC_TARGETEDIT].strData.ContainsAnyOf(",;")) + { + Unquote(CopyDlg[ID_SC_TARGETEDIT].strData); // уберем все лишние кавычки + InsertQuote(CopyDlg[ID_SC_TARGETEDIT].strData); // возьмем в кавычки, т.к. могут быть разделители + } + } + else + { + switch (DestPanelMode) + { + case NORMAL_PANEL: + { + if ((strDestDir.IsEmpty() || !DestPanel->IsVisible() || !StrCmp(strSrcDir,strDestDir)) && CDP.SelCount==1) + CopyDlg[ID_SC_TARGETEDIT].strData = strSelName; + else + { + CopyDlg[ID_SC_TARGETEDIT].strData = strDestDir; + AddEndSlash(CopyDlg[ID_SC_TARGETEDIT].strData); + } + + /* $ 19.07.2003 IS + Если цель содержит разделители, то возьмем ее в кавычки, дабы не получить + ерунду при F5, Enter в панелях, когда пользователь включит MultiCopy + */ + if (!Move && CopyDlg[ID_SC_TARGETEDIT].strData.ContainsAnyOf(",;")) + { + Unquote(CopyDlg[ID_SC_TARGETEDIT].strData); // уберем все лишние кавычки + InsertQuote(CopyDlg[ID_SC_TARGETEDIT].strData); // возьмем в кавычки, т.к. могут быть разделители + } + + break; + } + + case PLUGIN_PANEL: + { + OpenPluginInfo Info; + DestPanel->GetOpenPluginInfo(&Info); + FARString strFormat = Info.Format; + CopyDlg[ID_SC_TARGETEDIT].strData = strFormat+L":"; + + while (CopyDlg[ID_SC_TARGETEDIT].strData.GetLength()<2) + CopyDlg[ID_SC_TARGETEDIT].strData += L":"; + + CDP.strPluginFormat = CopyDlg[ID_SC_TARGETEDIT].strData; + CDP.strPluginFormat.Upper(); + break; + } + } + } + + FARString strInitDestDir = CopyDlg[ID_SC_TARGETEDIT].strData; + // Для фильтра + FAR_FIND_DATA_EX fd; + SrcPanel->GetSelNameCompat(nullptr,CDP.FileAttr); + + bool AddSlash=false; + + while (SrcPanel->GetSelNameCompat(&strSelName,CDP.FileAttr,&fd)) + { + if (UseFilter) + { + if (!Filter->FileInFilter(fd)) + continue; + } + + if (CDP.FileAttr & FILE_ATTRIBUTE_DIRECTORY) + { + CDP.FolderPresent=true; + AddSlash=true; +// break; + } + else + { + CDP.FilesPresent=true; + } + } + + if (Link) // рулесы по поводу линков (предварительные!) + { + for(int i=ID_SC_SEPARATOR3;i<=ID_SC_BTNCANCEL;i++) + { + CopyDlg[i].Y1-=3; + CopyDlg[i].Y2-=3; + } + CopyDlg[ID_SC_TITLE].Y2-=3; + DLG_HEIGHT-=3; + } + + // корректирем позицию " to" + CopyDlg[ID_SC_TARGETTITLE].X1=CopyDlg[ID_SC_TARGETTITLE].X2=CopyDlg[ID_SC_SOURCEFILENAME].X1+(int)CopyDlg[ID_SC_SOURCEFILENAME].strData.GetLength(); + + /* $ 15.06.2002 IS + Обработка копирования мышкой - в этом случае диалог не показывается, + но переменные все равно инициализируются. Если произойдет неудачная + компиляция списка целей, то покажем диалог. + */ + FARString strCopyDlgValue; + if (!Ask) + { + strCopyDlgValue = CopyDlg[ID_SC_TARGETEDIT].strData; + Unquote(strCopyDlgValue); + InsertQuote(strCopyDlgValue); + + if (!DestList.Set(strCopyDlgValue)) + Ask=TRUE; + } + + // *********************************************************************** + // *** Вывод и обработка диалога + // *********************************************************************** + if (Ask) + { + FarList ComboList; + FarListItem LinkTypeItems[2]={},CopyModeItems[8]={}; + + + if (Link) + { + ComboList.ItemsNumber=ARRAYSIZE(LinkTypeItems); + ComboList.Items=LinkTypeItems; + ComboList.Items[0].Text=MSG(MLinkTypeHardlink); + ComboList.Items[1].Text=MSG(MLinkTypeSymlink); + + ComboList.Items[CDP.FilesPresent ? 0 : 1].Flags|=LIF_SELECTED; + } + else + { + ComboList.ItemsNumber=ARRAYSIZE(CopyModeItems); + ComboList.Items=CopyModeItems; + ComboList.Items[CM_ASK].Text=MSG(MCopyAsk); + ComboList.Items[CM_OVERWRITE].Text=MSG(MCopyOverwrite); + ComboList.Items[CM_SKIP].Text=MSG(MCopySkipOvr); + ComboList.Items[CM_RENAME].Text=MSG(MCopyRename); + ComboList.Items[CM_APPEND].Text=MSG(MCopyAppend); + ComboList.Items[CM_ONLYNEWER].Text=MSG(MCopyOnlyNewerFiles); + ComboList.Items[CM_ASKRO].Text=MSG(MCopyAskRO); + ComboList.Items[CM_ASK].Flags=LIF_SELECTED; + ComboList.Items[CM_SEPARATOR].Flags=LIF_SEPARATOR; + + if (Opt.Confirm.RO) + { + ComboList.Items[CM_ASKRO].Flags=LIF_CHECKED; + } + } + + CopyDlg[ID_SC_COMBO].ListItems=&ComboList; + + Dialog Dlg(CopyDlg,ARRAYSIZE(CopyDlg),CopyDlgProc,(LONG_PTR)&CDP); + Dlg.SetHelp(Link?L"HardSymLink":L"CopyFiles"); + Dlg.SetId(Link?HardSymLinkId:(Move?MoveFilesId:CopyFilesId)); + Dlg.SetPosition(-1,-1,DLG_WIDTH,DLG_HEIGHT); + Dlg.SetAutomation(ID_SC_USEFILTER,ID_SC_BTNFILTER,DIF_DISABLE,DIF_NONE,DIF_NONE,DIF_DISABLE); +// Dlg.Show(); + // $ 02.06.2001 IS + Проверим список целей и поднимем тревогу, если он содержит ошибки + int DlgExitCode; + + for (;;) + { + Dlg.ClearDone(); + Dlg.Process(); + DlgExitCode=Dlg.GetExitCode(); + //Рефреш текущему времени для фильтра сразу после выхода из диалога + Filter->UpdateCurrentTime(); + + if (DlgExitCode == ID_SC_BTNCOPY) + { + /* $ 03.08.2001 IS + Запомним строчку из диалога и начинаем ее мучить в зависимости от + состояния опции мультикопирования + */ + strCopyDlgValue = CopyDlg[ID_SC_TARGETEDIT].strData; + if(!Move) + { + Opt.CMOpt.MultiCopy=CopyDlg[ID_SC_MULTITARGET].Selected; + } + Opt.CMOpt.WriteThrough=CopyDlg[ID_SC_WRITETHROUGH].Selected; + Opt.CMOpt.CopyAccessMode=CopyDlg[ID_SC_COPYACCESSMODE].Selected; + Opt.CMOpt.CopyXAttr=CopyDlg[ID_SC_COPYXATTR].Selected; + Opt.CMOpt.SparseFiles=CopyDlg[ID_SC_SPARSEFILES].Selected; + Opt.CMOpt.UseCOW=CopyDlg[ID_SC_USECOW].Selected; + + if (!CopyDlg[ID_SC_MULTITARGET].Selected || !strCopyDlgValue.ContainsAnyOf(",;")) // отключено multi* + { + // уберем лишние кавычки + Unquote(strCopyDlgValue); + // добавим кавычки, чтобы "список" удачно скомпилировался вне + // зависимости от наличия разделителей в оном + InsertQuote(strCopyDlgValue); + } + + if (DestList.Set(strCopyDlgValue)) + { + // Запомнить признак использования фильтра. KM + UseFilter=CopyDlg[ID_SC_USEFILTER].Selected; + break; + } + else + { + Message(MSG_WARNING,1,MSG(MWarning),MSG(MCopyIncorrectTargetList), MSG(MOk)); + } + } + else + break; + } + + if (DlgExitCode == ID_SC_BTNCANCEL || DlgExitCode < 0 || (CopyDlg[ID_SC_BTNCOPY].Flags&DIF_DISABLE)) + { + if (DestPlugin) + ToPlugin=-1; + + return; + } + } + + // *********************************************************************** + // *** Стадия подготовки данных после диалога + // *********************************************************************** + Flags.WRITETHROUGH = Flags.COPYACCESSMODE = Flags.COPYXATTR = Flags.SPARSEFILES = Flags.USECOW = false; + Flags.SYMLINK = COPY_SYMLINK_ASIS; + ReadOnlyDelMode = ReadOnlyOvrMode = OvrMode = SkipMode = SkipDeleteMode = -1; + + if (Link) + { + switch (CopyDlg[ID_SC_COMBO].ListPos) + { + case 0: + RPT=RP_HARDLINK; + break; + case 1: + RPT=RP_JUNCTION; + break; + case 2: + RPT=RP_SYMLINK; + break; + case 3: + RPT=RP_SYMLINKFILE; + break; + case 4: + RPT=RP_SYMLINKDIR; + break; + } + } + else + { + ReadOnlyOvrMode=CDP.AskRO?-1:1; + + switch (CopyDlg[ID_SC_COMBO].ListPos) + { + case CM_ASK: + OvrMode=-1; + break; + case CM_OVERWRITE: + OvrMode=1; + break; + case CM_SKIP: + OvrMode=3; + ReadOnlyOvrMode=CDP.AskRO?-1:3; + break; + case CM_RENAME: + OvrMode=5; + break; + case CM_APPEND: + OvrMode=7; + break; + case CM_ONLYNEWER: + Flags.ONLYNEWERFILES = true; + break; + } + + Opt.CMOpt.HowCopySymlink = CopyDlg[ID_SC_COPYSYMLINK_COMBO].ListPos; + switch (Opt.CMOpt.HowCopySymlink) + { + case 1: Flags.SYMLINK = COPY_SYMLINK_SMART; break; + case 2: Flags.SYMLINK = COPY_SYMLINK_ASFILE; break; + } + } + + if (DestPlugin && !StrCmp(CopyDlg[ID_SC_TARGETEDIT].strData,strInitDestDir)) + { + ToPlugin=1; + return; + } + + if (Opt.CMOpt.WriteThrough) + Flags.WRITETHROUGH = true; + if (Opt.CMOpt.CopyAccessMode) + Flags.COPYACCESSMODE = true; + if (Opt.CMOpt.CopyXAttr) + Flags.COPYXATTR = true; + if (Opt.CMOpt.SparseFiles) + Flags.SPARSEFILES = true; + if (Opt.CMOpt.UseCOW && Opt.CMOpt.SparseFiles == 0) + Flags.USECOW = true; + + if (CDP.SelCount==1) + AddSlash=false; //??? + + if (DestPlugin==2) + { + if (PluginDestPath) + strCopyDlgValue = PluginDestPath; + + return; + } + + if ((Opt.Diz.UpdateMode==DIZ_UPDATE_IF_DISPLAYED && SrcPanel->IsDizDisplayed()) || + Opt.Diz.UpdateMode==DIZ_UPDATE_ALWAYS) + { + CtrlObject->Cp()->LeftPanel->ReadDiz(); + CtrlObject->Cp()->RightPanel->ReadDiz(); + } + + DestPanel->CloseFile(); + strDestDizPath.Clear(); + SrcPanel->SaveSelection(); + // нужно ли показывать время копирования? + bool ShowCopyTime=(Opt.CMOpt.CopyTimeRule©_RULE_FILES)!=0; + // *********************************************************************** + // **** Здесь все подготовительные операции закончены, можно приступать + // **** к процессу Copy/Move/Link + // *********************************************************************** + int NeedDizUpdate=FALSE; + int NeedUpdateAPanel=FALSE; + Flags.UPDATEPPANEL = true; + /* + ЕСЛИ ПРИНЯТЬ В КАЧЕСТВЕ РАЗДЕЛИТЕЛЯ ПУТЕЙ, НАПРИМЕР ';', + то нужно парсить CopyDlgValue на предмет MultiCopy и + вызывать CopyFileTree нужное количество раз. + */ + { + Flags.MOVE = false; + + if (DestList.Set(strCopyDlgValue)) // если список успешно "скомпилировался" + { + const wchar_t *NamePtr; + FARString strNameTmp; + // посчитаем количество целей. + CountTarget=DestList.GetTotal(); + TotalFiles=0; + TotalCopySize=TotalCopiedSize=TotalSkippedSize=0; + ProgressUpdateTime=0; + + // Запомним время начала + if (ShowCopyTime) + { + CopyStartTime = GetProcessUptimeMSec(); + WaitUserTime = OldCalcTime = 0; + } + + if (CountTarget > 1) + Move=0; + + for( size_t DLI = 0; nullptr!=(NamePtr=DestList.Get(DLI)); ++DLI) + { + CurCopiedSize=0; + strNameTmp = NamePtr; + + if ((strNameTmp.GetLength() == 2) && IsAlpha(strNameTmp.At(0)) && (strNameTmp.At(1) == L':')) + PrepareDiskPath(strNameTmp); + + if (!StrCmp(strNameTmp,L"..") && IsLocalRootPath(strSrcDir)) + { + if (!Message(MSG_WARNING,2,MSG(MError),MSG((!Move?MCannotCopyToTwoDot:MCannotMoveToTwoDot)),MSG(MCannotCopyMoveToTwoDot),MSG(MCopySkip),MSG(MCopyCancel))) + continue; + + break; + } + + if (DestList.IsLastElement(DLI)) + { // для последней операции нужно учесть моменты связанные с операцией Move. + Flags.COPYLASTTIME = true; + if (Move) + Flags.MOVE = true; + } + + // Если выделенных элементов больше 1 и среди них есть каталог, то всегда + // делаем так, чтобы на конце был '/' + // деламем так не всегда, а только когда NameTmp не является маской. + if (AddSlash && !strNameTmp.ContainsAnyOf("*?")) + AddEndSlash(strNameTmp); + + if (CDP.SelCount==1 && !CDP.FolderPresent) + { + ShowTotalCopySize=false; + TotalFilesToProcess=1; + } + + if (Move) + { + if (CDP.SelCount==1 && CDP.FolderPresent && CheckUpdateAnotherPanel(SrcPanel,strSelName)) + { + NeedUpdateAPanel=TRUE; + } + } + + CP=new CopyProgress(Move!=0,ShowTotalCopySize,ShowCopyTime); + // Обнулим инфу про дизы + strDestDizPath.Clear(); + Flags.DIZREAD = false; + // сохраним выделение + SrcPanel->SaveSelection(); + const auto OldFlagsSYMLINK = Flags.SYMLINK; + // собственно - один проход копирования + // Mantis#45: Необходимо привсти копирование ссылок на папки с NTFS на FAT к более логичному виду + { + //todo: If dst does not support symlinks + //Flags.SYMLINK = COPY_SYMLINK_ASFILE; + } + PreRedraw.Push(PR_ShellCopyMsg); + PreRedrawItem preRedrawItem=PreRedraw.Peek(); + preRedrawItem.Param.Param1=CP; + PreRedraw.SetParam(preRedrawItem.Param); + int I=CopyFileTree(strNameTmp); + PreRedraw.Pop(); + Flags.SYMLINK = OldFlagsSYMLINK; + + if (I == COPY_CANCEL) + { + NeedDizUpdate=TRUE; + break; + } + + // если "есть порох в пороховницах" - восстановим выделение + if (!DestList.IsLastElement(DLI)) + SrcPanel->RestoreSelection(); + + // Позаботимся о дизах. + if (!strDestDizPath.IsEmpty()) + { + FARString strDestDizName; + DestDiz.GetDizName(strDestDizName); + DWORD Attr=apiGetFileAttributes(strDestDizName); + int DestReadOnly=(Attr!=INVALID_FILE_ATTRIBUTES && (Attr & FILE_ATTRIBUTE_READONLY)); + + if (DestList.IsLastElement(DLI)) // Скидываем только во время последней Op. + if (Move && !DestReadOnly) + SrcPanel->FlushDiz(); + + DestDiz.Flush(strDestDizPath); + } + } + } + _LOGCOPYR(else SysLog(L"Error: DestList.Set(CopyDlgValue) return FALSE")); + } + // *********************************************************************** + // *** заключительеая стадия процесса + // *** восстанавливаем/дизим/редравим + // *********************************************************************** + + if (NeedDizUpdate) // при мультикопировании может быть обрыв, но нам все + { // равно нужно апдейтить дизы! + if (!strDestDizPath.IsEmpty()) + { + FARString strDestDizName; + DestDiz.GetDizName(strDestDizName); + DWORD Attr=apiGetFileAttributes(strDestDizName); + int DestReadOnly=(Attr!=INVALID_FILE_ATTRIBUTES && (Attr & FILE_ATTRIBUTE_READONLY)); + + if (Move && !DestReadOnly) + SrcPanel->FlushDiz(); + + DestDiz.Flush(strDestDizPath); + } + } + + SrcPanel->Update(UPDATE_KEEP_SELECTION); + + if (CDP.SelCount==1 && !strRenamedName.IsEmpty()) + SrcPanel->GoToFile(strRenamedName); + + if (NeedUpdateAPanel && CDP.FileAttr != INVALID_FILE_ATTRIBUTES && (CDP.FileAttr&FILE_ATTRIBUTE_DIRECTORY) && DestPanelMode != PLUGIN_PANEL) + { + FARString strTmpSrcDir; + SrcPanel->GetCurDir(strTmpSrcDir); + DestPanel->SetCurDir(strTmpSrcDir,FALSE); + } + + // проверим "нужность" апдейта пассивной панели + if (Flags.UPDATEPPANEL) + { + DestPanel->SortFileList(TRUE); + DestPanel->Update(UPDATE_KEEP_SELECTION|UPDATE_SECONDARY); + } + + if (SrcPanelMode == PLUGIN_PANEL) + SrcPanel->SetPluginModified(); + + CtrlObject->Cp()->Redraw(); + + if (Opt.NotifOpt.OnFileOperation) { + DisplayNotification(MSG(MFileOperationComplete), strSelName); // looks like strSelName is best choice + } + +} + + +LONG_PTR WINAPI CopyDlgProc(HANDLE hDlg,int Msg,int Param1,LONG_PTR Param2) +{ +#define DM_CALLTREE (DM_USER+1) +#define DM_SWITCHRO (DM_USER+2) + CopyDlgParam *DlgParam=(CopyDlgParam *)SendDlgMessage(hDlg,DM_GETDLGDATA,0,0); + + switch (Msg) + { + case DN_INITDIALOG: + SendDlgMessage(hDlg,DM_SETCOMBOBOXEVENT,ID_SC_COMBO,CBET_KEY|CBET_MOUSE); + SendDlgMessage(hDlg,DM_SETMOUSEEVENTNOTIFY,TRUE,0); + break; + case DM_SWITCHRO: + { + FarListGetItem LGI={CM_ASKRO}; + SendDlgMessage(hDlg,DM_LISTGETITEM,ID_SC_COMBO,(LONG_PTR)&LGI); + + if (LGI.Item.Flags&LIF_CHECKED) + LGI.Item.Flags&=~LIF_CHECKED; + else + LGI.Item.Flags|=LIF_CHECKED; + + SendDlgMessage(hDlg,DM_LISTUPDATE,ID_SC_COMBO,(LONG_PTR)&LGI); + SendDlgMessage(hDlg,DM_REDRAW,0,0); + return TRUE; + } + case DN_BTNCLICK: + { + + if (Param1==ID_SC_USEFILTER) // "Use filter" + { + UseFilter=(int)Param2; + return TRUE; + } + + if (Param1 == ID_SC_BTNTREE) // Tree + { + SendDlgMessage(hDlg,DM_CALLTREE,0,0); + return FALSE; + } + else if (Param1 == ID_SC_BTNCOPY) + { + SendDlgMessage(hDlg,DM_CLOSE,ID_SC_BTNCOPY,0); + } + else if (Param1 == ID_SC_SPARSEFILES) + { + SendDlgMessage(hDlg,DM_SETCHECK,ID_SC_USECOW,BSTATE_UNCHECKED); + } + else if (Param1 == ID_SC_USECOW) + { + SendDlgMessage(hDlg,DM_SETCHECK,ID_SC_SPARSEFILES,BSTATE_UNCHECKED); + } + /* + else if(Param1 == ID_SC_ONLYNEWER && (DlgParam->thisClass->Flags.LINK)) + { + // подсократим код путем эмуляции телодвижений в строке ввода :-)) + SendDlgMessage(hDlg,DN_EDITCHANGE,ID_SC_TARGETEDIT,0); + } + */ + else if (Param1==ID_SC_BTNFILTER) // Filter + { + Filter->FilterEdit(); + return TRUE; + } + + break; + } + case DM_KEY: // по поводу дерева! + { + if (Param2 == KEY_ALTF10 || Param2 == KEY_F10 || Param2 == KEY_SHIFTF10) + { + DlgParam->AltF10=Param2 == KEY_ALTF10?1:(Param2 == KEY_SHIFTF10?2:0); + SendDlgMessage(hDlg,DM_CALLTREE,DlgParam->AltF10,0); + return TRUE; + } + + if (Param1 == ID_SC_COMBO) + { + if (Param2==KEY_ENTER || Param2==KEY_NUMENTER || Param2==KEY_INS || Param2==KEY_NUMPAD0 || Param2==KEY_SPACE) + { + if (SendDlgMessage(hDlg,DM_LISTGETCURPOS,ID_SC_COMBO,0)==CM_ASKRO) + return SendDlgMessage(hDlg,DM_SWITCHRO,0,0); + } + } + } + break; + + case DN_LISTHOTKEY: + if(Param1==ID_SC_COMBO) + { + if (SendDlgMessage(hDlg,DM_LISTGETCURPOS,ID_SC_COMBO,0)==CM_ASKRO) + { + SendDlgMessage(hDlg,DM_SWITCHRO,0,0); + return TRUE; + } + } + break; + case DN_MOUSEEVENT: + + if (SendDlgMessage(hDlg,DM_GETDROPDOWNOPENED,ID_SC_COMBO,0)) + { + MOUSE_EVENT_RECORD *mer=(MOUSE_EVENT_RECORD *)Param2; + + if (SendDlgMessage(hDlg,DM_LISTGETCURPOS,ID_SC_COMBO,0)==CM_ASKRO && mer->dwButtonState && !(mer->dwEventFlags&MOUSE_MOVED)) + { + SendDlgMessage(hDlg,DM_SWITCHRO,0,0); + return FALSE; + } + } + + break; + + case DM_CALLTREE: + { + /* $ 13.10.2001 IS + + При мультикопировании добавляем выбранный в "дереве" каталог к уже + существующему списку через точку с запятой. + - Баг: при мультикопировании выбранный в "дереве" каталог не + заключался в кавычки, если он содержал в своем + имени символы-разделители. + - Баг: неправильно работало Shift-F10, если строка ввода содержала + слеш на конце. + - Баг: неправильно работало Shift-F10 при мультикопировании - + показывался корневой каталог, теперь показывается самый первый каталог + в списке. + */ + BOOL MultiCopy=SendDlgMessage(hDlg,DM_GETCHECK,ID_SC_MULTITARGET,0)==BSTATE_CHECKED; + FARString strOldFolder; + int nLength; + FarDialogItemData Data; + nLength = (int)SendDlgMessage(hDlg, DM_GETTEXTLENGTH, ID_SC_TARGETEDIT, 0); + Data.PtrData = strOldFolder.GetBuffer(nLength+1); + Data.PtrLength = nLength; + SendDlgMessage(hDlg,DM_GETTEXT,ID_SC_TARGETEDIT,(LONG_PTR)&Data); + strOldFolder.ReleaseBuffer(); + FARString strNewFolder; + + if (DlgParam->AltF10 == 2) + { + strNewFolder = strOldFolder; + + if (MultiCopy) + { + UserDefinedList DestList(0,0,ULF_UNIQUE); + + if (DestList.Set(strOldFolder)) + { + const wchar_t *NamePtr=DestList.Get(0); + + if (NamePtr) + strNewFolder = NamePtr; + } + } + + if (strNewFolder.IsEmpty()) + DlgParam->AltF10=-1; + else // убираем лишний слеш + DeleteEndSlash(strNewFolder); + } + + if (DlgParam->AltF10 != -1) + { + { + FARString strNewFolder2; + FolderTree Tree(strNewFolder2, + (DlgParam->AltF10==1?MODALTREE_PASSIVE: + (DlgParam->AltF10==2?MODALTREE_FREE: + MODALTREE_ACTIVE)), + FALSE,FALSE); + strNewFolder = strNewFolder2; + } + + if (!strNewFolder.IsEmpty()) + { + AddEndSlash(strNewFolder); + + if (MultiCopy) // мультикопирование + { + // Добавим кавычки, если имя каталога содержит символы-разделители + if (strNewFolder.ContainsAnyOf(";,")) + InsertQuote(strNewFolder); + + if (strOldFolder.GetLength()) + strOldFolder += L";"; // добавим разделитель к непустому списку + + strOldFolder += strNewFolder; + strNewFolder = strOldFolder; + } + + SendDlgMessage(hDlg,DM_SETTEXTPTR,ID_SC_TARGETEDIT,(LONG_PTR)strNewFolder.CPtr()); + SendDlgMessage(hDlg,DM_SETFOCUS,ID_SC_TARGETEDIT,0); + } + } + + DlgParam->AltF10=0; + return TRUE; + } + case DN_CLOSE: + { + if (Param1==ID_SC_BTNCOPY) + { + FarListGetItem LGI={CM_ASKRO}; + SendDlgMessage(hDlg,DM_LISTGETITEM,ID_SC_COMBO,(LONG_PTR)&LGI); + + if (LGI.Item.Flags&LIF_CHECKED) + DlgParam->AskRO=TRUE; + } + } + break; + } + + return DefDlgProc(hDlg,Msg,Param1,Param2); +} + +ShellCopy::~ShellCopy() +{ + _tran(SysLog(L"[%p] ShellCopy::~ShellCopy(), CopyBufer=%p",this,CopyBuffer)); + + // $ 26.05.2001 OT Разрешить перерисовку панелей + _tran(SysLog(L"call (*FrameManager)[0]->UnlockRefresh()")); + (*FrameManager)[0]->Unlock(); + (*FrameManager)[0]->Refresh(); + + if (Filter) // Уничтожим объект фильтра + delete Filter; + + if (CP) + { + delete CP; + CP=nullptr; + } +} + + +COPY_CODES ShellCopy::CopyFileTree(const wchar_t *Dest) +{ + ChangePriority ChPriority(ChangePriority::NORMAL); + //SaveScreen SaveScr; + DWORD DestAttr=INVALID_FILE_ATTRIBUTES; + FARString strSelName; + int Length; + DWORD FileAttr; + + if (!(Length=StrLength(Dest)) || !StrCmp(Dest,L".")) + return COPY_FAILURE; //???? + + SetCursorType(FALSE,0); + + if (!TotalCopySize) + { + strTotalCopySizeText.Clear(); + + // ! Не сканируем каталоги при создании линков + if (ShowTotalCopySize && !Flags.LINK && !CalcTotalSize()) + return COPY_FAILURE; + } + else + { + CurCopiedSize=0; + } + + // Создание структуры каталогов в месте назначения + FARString strNewPath = Dest; + + if (!IsSlash(strNewPath.At(strNewPath.GetLength()-1)) && + SrcPanel->GetSelCount()>1 && + !strNewPath.ContainsAnyOf("*?") && + apiGetFileAttributes(strNewPath)==INVALID_FILE_ATTRIBUTES) + { + switch (Message(FMSG_WARNING,3,MSG(MWarning),strNewPath,MSG(MCopyDirectoryOrFile),MSG(MCopyDirectoryOrFileDirectory),MSG(MCopyDirectoryOrFileFile),MSG(MCancel))) + { + case 0: + AddEndSlash(strNewPath); + break; + case -2: + case -1: + case 2: + return COPY_CANCEL; + } + } + + size_t pos; + + if (FindLastSlash(pos,strNewPath)) + { + strNewPath.Truncate(pos+1); + + DWORD Attr=apiGetFileAttributes(strNewPath); + + if (Attr==INVALID_FILE_ATTRIBUTES) + { + if (apiCreateDirectory(strNewPath,nullptr)) + TreeList::AddTreeName(strNewPath); + else + CreatePath(strNewPath); + } + else if (!(Attr & FILE_ATTRIBUTE_DIRECTORY)) + { + Message(MSG_WARNING,1,MSG(MError),MSG(MCopyCannotCreateFolder),strNewPath,MSG(MOk)); + return COPY_FAILURE; + } + } + + DestAttr=apiGetFileAttributes(Dest); + + // Выставим признак "Тот же диск" + bool AllowMoveByOS=false; + + if (Flags.MOVE) + { + FARString strTmpSrcDir; + SrcPanel->GetCurDir(strTmpSrcDir); + AllowMoveByOS = (CheckDisksProps(strTmpSrcDir,Dest,CHECKEDPROPS_ISSAMEDISK)) != 0; + } + + + // Основной цикл копирования одной порции. + SrcPanel->GetSelNameCompat(nullptr,FileAttr); + while (SrcPanel->GetSelNameCompat(&strSelName,FileAttr)) + { + SelectedPanelItems.emplace_back(); + ConvertNameToFull(strSelName, SelectedPanelItems.back()); + } + + SrcPanel->GetSelNameCompat(nullptr,FileAttr); + { + while (SrcPanel->GetSelNameCompat(&strSelName,FileAttr)) + { + FARString strDest = Dest; + + if (FileAttr & FILE_ATTRIBUTE_DIRECTORY) + SelectedFolderNameLength=(int)strSelName.GetLength(); + else + SelectedFolderNameLength=0; + + if (strDest.ContainsAnyOf("*?")) + ConvertWildcards(strSelName, strDest, SelectedFolderNameLength); + + DestAttr=apiGetFileAttributes(strDest); + + FARString strDestPath = strDest; + FAR_FIND_DATA_EX SrcData; SrcData.Clear(); + int CopyCode=COPY_SUCCESS,KeepPathPos; + Flags.OVERWRITENEXT = false; + + KeepPathPos=(int)(PointToName(strSelName)-strSelName.CPtr()); + + if (RPT==RP_JUNCTION || RPT==RP_SYMLINK || RPT==RP_SYMLINKFILE || RPT==RP_SYMLINKDIR) + { + switch (MkSymLink(strSelName,strDest,RPT,true)) + { + case 2: + break; + case 1: + + // Отметим (Ins) несколько каталогов, ALT-F6 Enter - выделение с папок не снялось. + if (!Flags.CURRENTONLY && Flags.COPYLASTTIME) + SrcPanel->ClearLastGetSelection(); + + continue; + case 0: + return COPY_FAILURE; + } + } + else + { + // проверка на вшивость ;-) + + if (!apiGetFindDataForExactPathName(strSelName,SrcData)) + { + strDestPath = strSelName; + CP->SetNames(strSelName,strDestPath); + + if (Message(MSG_WARNING,2,MSG(MError),MSG(MCopyCannotFind), + strSelName,MSG(MSkip),MSG(MCancel))==1) + { + return COPY_FAILURE; + } + + continue; + } +///fprintf(stderr, "!!!!! RPT=%x for '%ls'\n", (SrcData.dwFileAttributes&FILE_ATTRIBUTE_REPARSE_POINT), strSelName.CPtr()); + } + + + //KeepPathPos=PointToName(SelName)-SelName; + + // Мувим? + if (Flags.MOVE) + { + // Тыкс, а как на счет "тот же диск"? + if (KeepPathPos && PointToName(strDest)==strDest) + { + strDestPath = strSelName; + strDestPath.Truncate(KeepPathPos); + strDestPath += strDest; + AllowMoveByOS=true; + } + + if (UseFilter || !AllowMoveByOS || //can't move across different devices + //if any symlinks copy may occur - parse whole tree + ( (SrcData.dwFileAttributes & (FILE_ATTRIBUTE_REPARSE_POINT|FILE_ATTRIBUTE_DIRECTORY)) != 0 && Flags.SYMLINK != COPY_SYMLINK_ASIS)) + { + CopyCode=COPY_FAILURE; + } + else + { + CopyCode=ShellCopyOneFile(strSelName,SrcData,strDestPath,KeepPathPos,1); + + + if (CopyCode==COPY_SUCCESS_MOVE) + { + if (!strDestDizPath.IsEmpty()) + { + if (!strRenamedName.IsEmpty()) + { + DestDiz.DeleteDiz(strSelName); + SrcPanel->CopyDiz(strSelName,strRenamedName,&DestDiz); + } + else + { + if (strCopiedName.IsEmpty()) + strCopiedName = strSelName; + + SrcPanel->CopyDiz(strSelName,strCopiedName,&DestDiz); + SrcPanel->DeleteDiz(strSelName); + } + } + + continue; + } + + if (CopyCode==COPY_CANCEL) + return COPY_CANCEL; + + if (CopyCode==COPY_NEXT) + { + uint64_t CurSize = SrcData.nFileSize; + TotalCopiedSize = TotalCopiedSize - CurCopiedSize + CurSize; + TotalSkippedSize = TotalSkippedSize + CurSize - CurCopiedSize; + continue; + } + + if (!Flags.MOVE || CopyCode==COPY_FAILURE) + Flags.OVERWRITENEXT = true; + } + } + + if (!Flags.MOVE || CopyCode==COPY_FAILURE) + { + FARString strCopyDest=strDest; + + CopyCode=ShellCopyOneFile(strSelName,SrcData,strCopyDest,KeepPathPos,0); + + Flags.OVERWRITENEXT = false; + + if (CopyCode==COPY_CANCEL) + return COPY_CANCEL; + + if (CopyCode!=COPY_SUCCESS) + { + uint64_t CurSize = SrcData.nFileSize; + + if (CopyCode != COPY_NOFILTER) //???? + TotalCopiedSize = TotalCopiedSize - CurCopiedSize + CurSize; + + if (CopyCode == COPY_NEXT) + TotalSkippedSize = TotalSkippedSize + CurSize - CurCopiedSize; + + continue; + } + } + + if (CopyCode==COPY_SUCCESS && !strDestDizPath.IsEmpty()) + { + if (strCopiedName.IsEmpty()) + strCopiedName = strSelName; + + SrcPanel->CopyDiz(strSelName,strCopiedName,&DestDiz); + } + + + // Mantis#44 - Потеря данных при копировании ссылок на папки + // если каталог (или нужно копировать симлинк) - придется рекурсивно спускаться... + if (RPT!=RP_SYMLINKFILE && (SrcData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 && + ( + (SrcData.dwFileAttributes&FILE_ATTRIBUTE_REPARSE_POINT) == 0 || + ((SrcData.dwFileAttributes & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_BROKEN)) == FILE_ATTRIBUTE_REPARSE_POINT && Flags.SYMLINK == COPY_SYMLINK_ASFILE) + ) + ) + { + int SubCopyCode; + FARString strSubName; + FARString strFullName; + ScanTree ScTree(TRUE, TRUE, Flags.SYMLINK == COPY_SYMLINK_ASFILE); + strSubName = strSelName; + strSubName += L"/"; + + if (DestAttr==INVALID_FILE_ATTRIBUTES) + KeepPathPos=(int)strSubName.GetLength(); + + int NeedRename=!((SrcData.dwFileAttributes&FILE_ATTRIBUTE_REPARSE_POINT) && Flags.SYMLINK == COPY_SYMLINK_ASFILE && Flags.MOVE); + ScTree.SetFindPath(strSubName,L"*",FSCANTREE_FILESFIRST); + while (ScTree.GetNextName(&SrcData,strFullName)) + { + if (UseFilter && (SrcData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + // Просто пропустить каталог недостаточно - если каталог помечен в + // фильтре как некопируемый, то следует пропускать и его и всё его + // содержимое. + if (!Filter->FileInFilter(SrcData)) + { + ScTree.SkipDir(); + continue; + } + } + { + int AttemptToMove=FALSE; + + if (Flags.MOVE && !UseFilter && AllowMoveByOS + && (SrcData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0 + && ((SrcData.dwFileAttributes & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_BROKEN)) == 0 || Flags.SYMLINK == COPY_SYMLINK_ASIS)) + { + AttemptToMove=TRUE; + int Ret=COPY_SUCCESS; + FARString strCopyDest=strDest; + + Ret=ShellCopyOneFile(strFullName,SrcData,strCopyDest,KeepPathPos,NeedRename); + + switch (Ret) // 1 + { + case COPY_CANCEL: + return COPY_CANCEL; + case COPY_NEXT: + { + uint64_t CurSize = SrcData.nFileSize; + TotalCopiedSize = TotalCopiedSize - CurCopiedSize + CurSize; + TotalSkippedSize = TotalSkippedSize + CurSize - CurCopiedSize; + continue; + } + case COPY_SUCCESS_MOVE: + { + continue; + } + case COPY_SUCCESS: + + if (!NeedRename) // вариант при перемещении содержимого симлика с опцией "копировать содержимое сим..." + { + uint64_t CurSize = SrcData.nFileSize; + TotalCopiedSize = TotalCopiedSize - CurCopiedSize + CurSize; + TotalSkippedSize = TotalSkippedSize + CurSize - CurCopiedSize; + continue; // ... т.к. мы ЭТО не мувили, а скопировали, то все, на этом закончим бадаться с этим файлов + } + } + } + + int SaveOvrMode=OvrMode; + + if (AttemptToMove) + OvrMode=1; + + FARString strCopyDest=strDest; + + SubCopyCode=ShellCopyOneFile(strFullName,SrcData,strCopyDest,KeepPathPos,0); + + if (AttemptToMove) + OvrMode=SaveOvrMode; + } + + if (SubCopyCode==COPY_CANCEL) + return COPY_CANCEL; + + if (SubCopyCode==COPY_NEXT) + { + uint64_t CurSize = SrcData.nFileSize; + TotalCopiedSize = TotalCopiedSize - CurCopiedSize + CurSize; + TotalSkippedSize = TotalSkippedSize + CurSize - CurCopiedSize; + } + + if (SubCopyCode==COPY_SUCCESS) + { + if (Flags.MOVE) + { + if (SrcData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if (ScTree.IsDirSearchDone() || + ((SrcData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && (Flags.SYMLINK != COPY_SYMLINK_ASFILE))) + { + TemporaryMakeWritable tmw(strFullName); + //if (SrcData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) + // apiMakeWritable(strFullName); //apiSetFileAttributes(strFullName,FILE_ATTRIBUTE_NORMAL); + + if (apiRemoveDirectory(strFullName)) + TreeList::DelTreeName(strFullName); + } + } + // здесь нужны проверка на FSCANTREE_INSIDEJUNCTION, иначе + // при мовинге будет удаление файла, что крайне неправильно! + else if (!ScTree.IsInsideSymlink()) + { + if (DeleteAfterMove(strFullName,SrcData.dwFileAttributes)==COPY_CANCEL) + return COPY_CANCEL; + } + } + } + } + + if (Flags.MOVE && CopyCode==COPY_SUCCESS) + { + TemporaryMakeWritable tmw(strSelName); + //if (FileAttr & FILE_ATTRIBUTE_READONLY) + // apiMakeWritable(strSelName); //apiSetFileAttributes(strSelName,FILE_ATTRIBUTE_NORMAL); + + if (apiRemoveDirectory(strSelName)) + { + TreeList::DelTreeName(strSelName); + + if (!strDestDizPath.IsEmpty()) + SrcPanel->DeleteDiz(strSelName); + } + } + } + else if (Flags.MOVE && CopyCode==COPY_SUCCESS) + { + int DeleteCode; + + if ((DeleteCode=DeleteAfterMove(strSelName,FileAttr))==COPY_CANCEL) + return COPY_CANCEL; + + if (DeleteCode==COPY_SUCCESS && !strDestDizPath.IsEmpty()) + SrcPanel->DeleteDiz(strSelName); + } + + if (!Flags.CURRENTONLY && Flags.COPYLASTTIME) + { + SrcPanel->ClearLastGetSelection(); + } + } + } + return COPY_SUCCESS; //COPY_SUCCESS_MOVE??? +} + + + +bool ShellCopy::IsSymlinkTargetAlsoCopied(const wchar_t *SymLink) +{ + if (!SymLink || !*SymLink) + return false; + + FARString strTarget; + ConvertNameToReal(SymLink, strTarget); + for (const auto &strItem : SelectedPanelItems) + { + if (strTarget == strItem) + { +// fprintf(stderr, "%s('%ls'): TRUE, '%ls' matches '%ls'\n", __FUNCTION__, SymLink, strTarget.CPtr(), strItem.CPtr()); + return true; + } + FARString strItemReal; + ConvertNameToReal(strItem, strItemReal); + + if (strTarget.GetLength() > strItemReal.GetLength() + && strTarget[strItemReal.GetLength()] == GOOD_SLASH && strTarget.Begins(strItemReal)) + { +// fprintf(stderr, "%s('%ls'): TRUE, '%ls' under '%ls'\n", __FUNCTION__, SymLink, strTarget.CPtr(), strItemReal.CPtr()); + return true; + } + } + +// fprintf(stderr, "IsSymlinkTargetAlsoCopied('%ls'): FALSE, '%ls'\n", SymLink, strTarget.CPtr()); + return false; +} + +COPY_CODES ShellCopy::CreateSymLink(const char *Target, const wchar_t *NewName, const FAR_FIND_DATA_EX &SrcData) +{ + if (apiIsDevNull(NewName)) + return COPY_SUCCESS; + + int r = sdc_symlink( Target, Wide2MB(NewName).c_str()); + if (r == 0) + return COPY_SUCCESS; + + if (errno == EEXIST ) { + int RetCode = 0, Append = 0; + FARString strNewName = NewName, strTarget = Target; + if (AskOverwrite(SrcData, strTarget, NewName, 0, 0, 0, 0, Append, strNewName, RetCode)) { + if (strNewName == NewName) { + fprintf(stderr, + "CreateSymLink('%s', '%ls') - overwriting and strNewName='%ls'\n", + Target, NewName, strNewName.CPtr()); + sdc_remove(strNewName.GetMB().c_str() ); + + } else { + fprintf(stderr, + "CreateSymLink('%s', '%ls') - renaming and strNewName='%ls'\n", + Target, NewName, strNewName.CPtr()); + } + return CreateSymLink(Target, strNewName.CPtr(), SrcData); + } + + return (COPY_CODES)RetCode; + } + + + switch (Message(MSG_WARNING, 3 ,MSG(MError), + MSG(MCopyCannotCreateSymlinkAskCopyContents), + NewName, MSG(MYes), MSG(MSkip), MSG(MCancel) )) + { + case 0: + Flags.SYMLINK = COPY_SYMLINK_ASFILE; + return COPY_RETRY; + + case 1: + return COPY_FAILURE; + + case 2: + default: + return COPY_CANCEL; + } +} + +COPY_CODES ShellCopy::CopySymLink(const wchar_t *ExistingName, const wchar_t *NewName, const FAR_FIND_DATA_EX &SrcData) +{ + FARString strExistingName; + ConvertNameToFull(ExistingName, strExistingName); + + //fprintf(stderr, "CopySymLink('%ls', '%ls', '%ls') '%ls'\n", Root, ExistingName, NewName, strRealName.CPtr()); + const std::string &mbExistingName = strExistingName.GetMB(); + char LinkTarget[PATH_MAX + 1]; + ssize_t r = sdc_readlink(mbExistingName.c_str(), LinkTarget, sizeof(LinkTarget) - 1); + if (r <= 0 || r >= (ssize_t)sizeof(LinkTarget) || LinkTarget[0] == 0) { + fprintf(stderr, "CopySymLink: r=%ld errno=%u from sdc_readlink('%ls')\n", (long)r, errno, strExistingName.CPtr()); + return COPY_FAILURE; + } + + LinkTarget[r] = 0; + + // create exactly same symlink as existing one in following cases: + // - if settings specifies to not be smart + // - if existing symlink is relative + // - if existing symlink points to unexisting destination that is also out or set of files being copied + // note that in case of being smart and if symlink is relative then caller + // guarantees that its target is within copied tree, so link will be valid + if (Flags.SYMLINK != COPY_SYMLINK_SMART || LinkTarget[0] != '/' + || ((SrcData.dwFileAttributes&FILE_ATTRIBUTE_BROKEN) != 0 && !IsSymlinkTargetAlsoCopied(ExistingName))) { + FARString strNewName; + ConvertNameToFull(NewName, strNewName); + return CreateSymLink(LinkTarget, strNewName.CPtr(), SrcData); + } + + // this is a case of smart linking - create symlink that relatively points to _new_ target + // by applying to new link relative path from existing symlink to its existing target + FARString strRealName; + ConvertNameToReal(ExistingName, strRealName); + std::vector<std::string> partsRealName, partsExistingName; + StrExplode(partsRealName, strRealName.GetMB(), "/"); + StrExplode(partsExistingName, strExistingName.GetMB(), "/"); + + size_t common_anchestors_count = 0; + while (common_anchestors_count < partsRealName.size() + && common_anchestors_count < partsExistingName.size() + && partsRealName[common_anchestors_count] == partsExistingName[common_anchestors_count]) { + ++common_anchestors_count; + } + + std::string relative_target; + for (size_t i = common_anchestors_count; i + 1 < partsExistingName.size(); ++i) { + relative_target+= "../"; + } + for (size_t i = common_anchestors_count; i < partsRealName.size(); ++i) { + relative_target+= partsRealName[i]; + if (i + 1 < partsRealName.size()) { + relative_target+= '/'; + } + } + if (relative_target.empty()) { + fprintf(stderr, + "CopySymLink: empty relative_target strRealName='%ls' strExistingName='%ls'\n", + strRealName.CPtr(), strExistingName.CPtr()); + } + + return CreateSymLink(relative_target.c_str(), NewName, SrcData); +} + +COPY_CODES ShellCopy::ShellCopyOneFile( + const wchar_t *Src, + const FAR_FIND_DATA_EX &SrcData, + FARString &strDest, + int KeepPathPos, + int Rename +) +{ + for (;;) { + COPY_CODES out = ShellCopyOneFileNoRetry(Src, SrcData, strDest, KeepPathPos, Rename); + if (out != COPY_RETRY) + return out; + } +} + +COPY_CODES ShellCopy::ShellCopyOneFileNoRetry( + const wchar_t *Src, + const FAR_FIND_DATA_EX &SrcData, + FARString &strDest, + int KeepPathPos, + int Rename +) +{ + CurCopiedSize = 0; // Сбросить текущий прогресс + + if (CP->Cancelled()) + { + return(COPY_CANCEL); + } + + if (UseFilter) + { + if (!Filter->FileInFilter(SrcData)) + return COPY_NOFILTER; + } + + FARString strDestPath = strDest; + const wchar_t *NamePtr = PointToName(strDestPath); + DWORD DestAttr = (strDestPath == L"/" || !*NamePtr || TestParentFolderName(NamePtr)) + ? FILE_ATTRIBUTE_DIRECTORY : INVALID_FILE_ATTRIBUTES; + + FAR_FIND_DATA_EX DestData; DestData.Clear(); + if (DestAttr==INVALID_FILE_ATTRIBUTES) + { + if (apiGetFindDataForExactPathName(strDestPath,DestData)) + DestAttr=DestData.dwFileAttributes; + } + + int SameName=0, Append=0; + + if (DestAttr!=INVALID_FILE_ATTRIBUTES && (DestAttr & FILE_ATTRIBUTE_DIRECTORY)) + { + int CmpCode=CmpFullNames(Src,strDestPath); + + if(CmpCode && SrcData.dwFileAttributes&FILE_ATTRIBUTE_REPARSE_POINT && RPT==RP_EXACTCOPY && Flags.SYMLINK != COPY_SYMLINK_ASFILE) + { + CmpCode = 0; + } + + if (CmpCode==1) // TODO: error check + { + SameName=1; + + if (Rename) + { + CmpCode=!StrCmp(PointToName(Src),PointToName(strDestPath)); + } + + if (CmpCode==1) + { + SetMessageHelp(L"ErrCopyItSelf"); + Message(MSG_WARNING,1,MSG(MError),MSG(MCannotCopyFolderToItself1), + Src,MSG(MCannotCopyFolderToItself2),MSG(MOk)); + return(COPY_CANCEL); + } + } + + if (!SameName) + { + int Length=(int)strDestPath.GetLength(); + + if (!IsSlash(strDestPath.At(Length-1)) && strDestPath.At(Length-1)!=L':') + strDestPath += L"/"; + + const wchar_t *PathPtr=Src+KeepPathPos; + + if (*PathPtr && !KeepPathPos && PathPtr[1]==L':') + PathPtr+=2; + + if (IsSlash(*PathPtr)) + PathPtr++; + + strDestPath += PathPtr; + + if (!apiGetFindDataForExactPathName(strDestPath,DestData)) + DestAttr=INVALID_FILE_ATTRIBUTES; + else + DestAttr=DestData.dwFileAttributes; + } + } + + SetDestDizPath(strDestPath); + + CP->SetProgressValue(0,0); + CP->SetNames(Src,strDestPath); + + const bool copy_sym_link = (RPT == RP_EXACTCOPY && + (SrcData.dwFileAttributes&FILE_ATTRIBUTE_REPARSE_POINT) != 0 && + ( (SrcData.dwFileAttributes&FILE_ATTRIBUTE_BROKEN) != 0 + || (Flags.SYMLINK != COPY_SYMLINK_ASFILE && + (Flags.SYMLINK != COPY_SYMLINK_SMART || IsSymlinkTargetAlsoCopied(Src)) )) ); + + if ((SrcData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) != 0 || copy_sym_link) + { + if (!Rename) + strCopiedName = PointToName(strDestPath); + + if (DestAttr!=INVALID_FILE_ATTRIBUTES) + { + if ((DestAttr & FILE_ATTRIBUTE_DIRECTORY) && !SameName) + { + // TODO: apply SrcData.dwUnixMode to strDestPath here or at CreateDirectory + FARString strSrcFullName; + ConvertNameToFull(Src,strSrcFullName); + return(!StrCmp(strDestPath,strSrcFullName) ? COPY_NEXT:COPY_SUCCESS); + } + + int Type=apiGetFileTypeByName(strDestPath); + + if (Type==FILE_TYPE_CHAR || Type==FILE_TYPE_PIPE) + return(Rename ? COPY_NEXT:COPY_SUCCESS); + } + + if (Rename) + { + FARString strSrcFullName,strDestFullName; + ConvertNameToFull(Src,strSrcFullName); + + // Пытаемся переименовать, пока не отменят + for (;;) + { + BOOL SuccessMove=apiMoveFile(Src,strDestPath); + + if (SuccessMove) + { + if (PointToName(strDestPath)==strDestPath.CPtr()) + strRenamedName = strDestPath; + else + strCopiedName = PointToName(strDestPath); + + ConvertNameToFull(strDest, strDestFullName); + TreeList::RenTreeName(strSrcFullName,strDestFullName); + return(SameName ? COPY_NEXT:COPY_SUCCESS_MOVE); + } + else + { + int MsgCode = Message(MSG_WARNING|MSG_ERRORTYPE,3,MSG(MError), + MSG(MCopyCannotRenameFolder),Src,MSG(MCopyRetry), + MSG(MCopyIgnore),MSG(MCopyCancel)); + + switch (MsgCode) + { + case 0: continue; + case 1: + { + + if (apiCreateDirectory(strDestPath, nullptr)) + { + if (PointToName(strDestPath)==strDestPath.CPtr()) + strRenamedName = strDestPath; + else + strCopiedName = PointToName(strDestPath); + + TreeList::AddTreeName(strDestPath); + return(COPY_SUCCESS); + } + } + default: + return (COPY_CANCEL); + } /* switch */ + } /* else */ + } /* while */ + } // if (Rename) + if (RPT!=RP_SYMLINKFILE && + (SrcData.dwFileAttributes & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY)) == FILE_ATTRIBUTE_DIRECTORY) + { + while (!apiCreateDirectory(strDestPath, nullptr)) + { + int MsgCode=Message(MSG_WARNING|MSG_ERRORTYPE,3,MSG(MError), + MSG(MCopyCannotCreateFolder),strDestPath,MSG(MCopyRetry), + MSG(MCopySkip),MSG(MCopyCancel)); + + if (MsgCode) + return((MsgCode==-2 || MsgCode==2) ? COPY_CANCEL:COPY_NEXT); + } + } + // [ ] Copy contents of symbolic links + if (copy_sym_link) + { + COPY_CODES CopyRetCode = CopySymLink(Src, strDestPath, SrcData); + if (CopyRetCode != COPY_SUCCESS && CopyRetCode != COPY_SUCCESS_MOVE) + return CopyRetCode; + } + TreeList::AddTreeName(strDestPath); + return COPY_SUCCESS; + } + + if (DestAttr!=INVALID_FILE_ATTRIBUTES && !(DestAttr & FILE_ATTRIBUTE_DIRECTORY)) + { + if (SrcData.nFileSize==DestData.nFileSize) + { + int CmpCode=CmpFullNames(Src,strDestPath); + + if(CmpCode && SrcData.dwFileAttributes&FILE_ATTRIBUTE_REPARSE_POINT && RPT==RP_EXACTCOPY && Flags.SYMLINK != COPY_SYMLINK_ASFILE) + { + CmpCode = 0; + } + + if (CmpCode==1) // TODO: error check + { + SameName=1; + + if (Rename) + { + CmpCode=!StrCmp(PointToName(Src),PointToName(strDestPath)); + } + + if (CmpCode==1 && !Rename) + { + Message(MSG_WARNING,1,MSG(MError),MSG(MCannotCopyFileToItself1), + Src,MSG(MCannotCopyFileToItself2),MSG(MOk)); + return(COPY_CANCEL); + } + } + } + int RetCode=0; + FARString strNewName; + + if (!AskOverwrite(SrcData,Src,strDestPath,DestAttr,SameName,Rename,(Flags.LINK?0:1),Append,strNewName,RetCode)) + { + return((COPY_CODES)RetCode); + } + + if (RetCode==COPY_RETRY) + { + strDest=strNewName; + + if (CutToSlash(strNewName) && apiGetFileAttributes(strNewName)==INVALID_FILE_ATTRIBUTES) + { + CreatePath(strNewName); + } + + return COPY_RETRY; + } + } + + for (;;) + { + int CopyCode=0; + uint64_t SaveTotalSize=TotalCopiedSize; + + if (Rename) + { + int MoveCode=FALSE,AskDelete; + + if (!Append) + { + FARString strSrcFullName; + ConvertNameToFull(Src,strSrcFullName); + + MoveCode = apiMoveFileEx(strSrcFullName, strDestPath, + SameName ? MOVEFILE_COPY_ALLOWED : MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING); + + if (!MoveCode && WINPORT(GetLastError)() == ERROR_NOT_SAME_DEVICE) + { + return COPY_FAILURE; + } + + if (ShowTotalCopySize && MoveCode) + { + TotalCopiedSize+=SrcData.nFileSize; + CP->SetTotalProgressValue(TotalCopiedSize,TotalCopySize); + } + + AskDelete=0; + } + else + { + do + { + CopyCode=ShellCopyFile(Src,SrcData,strDestPath,Append); + } + while (CopyCode==COPY_RETRY); + + switch (CopyCode) + { + case COPY_SUCCESS: + MoveCode=TRUE; + break; + case COPY_FAILUREREAD: + case COPY_FAILURE: + MoveCode=FALSE; + break; + case COPY_CANCEL: + return COPY_CANCEL; + case COPY_NEXT: + return COPY_NEXT; + } + + AskDelete=1; + } + + if (MoveCode) + { + if (DestAttr==INVALID_FILE_ATTRIBUTES || !(DestAttr & FILE_ATTRIBUTE_DIRECTORY)) + { + if (PointToName(strDestPath)==strDestPath.CPtr()) + strRenamedName = strDestPath; + else + strCopiedName = PointToName(strDestPath); + } + + TotalFiles++; + + if (AskDelete && DeleteAfterMove(Src,SrcData.dwFileAttributes)==COPY_CANCEL) + return COPY_CANCEL; + + return(COPY_SUCCESS_MOVE); + } + } + else + { + do + { + CopyCode=ShellCopyFile(Src,SrcData,strDestPath,Append); + } + while (CopyCode==COPY_RETRY); + + if (Append && DestData.dwUnixMode != 0 && (CopyCode != COPY_SUCCESS || DestData.dwUnixMode != SrcData.dwUnixMode)) + { + const std::string &mbDestPath = strDestPath.GetMB(); + sdc_chmod(mbDestPath.c_str(), DestData.dwUnixMode); + } + if (CopyCode==COPY_SUCCESS) + { + strCopiedName = PointToName(strDestPath); + TotalFiles++; + return COPY_SUCCESS; + } + else if (CopyCode==COPY_CANCEL || CopyCode==COPY_NEXT) + { + return((COPY_CODES)CopyCode); + } + } + + //???? + if (CopyCode == COPY_FAILUREREAD) + return COPY_FAILURE; + + //???? + FARString strMsg1, strMsg2; + int MsgMCannot=Flags.LINK ? MCannotLink: Flags.MOVE ? MCannotMove: MCannotCopy; + strMsg1 = Src; + strMsg2 = strDestPath; + InsertQuote(strMsg1); + InsertQuote(strMsg2); + + int MsgCode; + + if (SkipMode!=-1) + MsgCode=SkipMode; + else + { + MsgCode=Message(MSG_WARNING|MSG_ERRORTYPE,4,MSG(MError), + MSG(MsgMCannot), + strMsg1, + MSG(MCannotCopyTo), + strMsg2, + MSG(MCopyRetry),MSG(MCopySkip), + MSG(MCopySkipAll),MSG(MCopyCancel)); + } + + switch (MsgCode) + { + case -1: + case 1: + return COPY_NEXT; + case 2: + SkipMode=1; + return COPY_NEXT; + case -2: + case 3: + return COPY_CANCEL; + } + + TotalCopiedSize=SaveTotalSize; + int RetCode; + FARString strNewName; + + if (!AskOverwrite(SrcData,Src,strDestPath,DestAttr,SameName,Rename,(Flags.LINK?0:1),Append,strNewName,RetCode)) + return((COPY_CODES)RetCode); + + if (RetCode==COPY_RETRY) + { + strDest=strNewName; + + if (CutToSlash(strNewName) && apiGetFileAttributes(strNewName)==INVALID_FILE_ATTRIBUTES) + { + CreatePath(strNewName); + } + + return COPY_RETRY; + } + } +} + +int ShellCopy::DeleteAfterMove(const wchar_t *Name,DWORD Attr) +{ + if (Attr & FILE_ATTRIBUTE_READONLY) + { + int MsgCode; + + if (!Opt.Confirm.RO) + ReadOnlyDelMode=1; + + if (ReadOnlyDelMode!=-1) + MsgCode=ReadOnlyDelMode; + else + MsgCode=Message(MSG_WARNING,5,MSG(MWarning), + MSG(MCopyFileRO),Name,MSG(MCopyAskDelete), + MSG(MCopyDeleteRO),MSG(MCopyDeleteAllRO), + MSG(MCopySkipRO),MSG(MCopySkipAllRO),MSG(MCopyCancelRO)); + + switch (MsgCode) + { + case 1: + ReadOnlyDelMode=1; + break; + case 2: + return(COPY_NEXT); + case 3: + ReadOnlyDelMode=3; + return(COPY_NEXT); + case -1: + case -2: + case 4: + return(COPY_CANCEL); + } + } + + TemporaryMakeWritable tmw(Name); + + while ((Attr&FILE_ATTRIBUTE_DIRECTORY)?!apiRemoveDirectory(Name):!apiDeleteFile(Name)) + { + int MsgCode; + + if (SkipDeleteMode!=-1) + MsgCode=SkipDeleteMode; + else + MsgCode=Message(MSG_WARNING|MSG_ERRORTYPE,4,MSG(MError),MSG(MCannotDeleteFile),Name, + MSG(MDeleteRetry),MSG(MDeleteSkip),MSG(MDeleteSkipAll),MSG(MDeleteCancel)); + + switch (MsgCode) + { + case 1: + return COPY_NEXT; + case 2: + SkipDeleteMode=1; + return COPY_NEXT; + case -1: + case -2: + case 3: + return(COPY_CANCEL); + } + } + + return(COPY_SUCCESS); +} + +static void ProgressUpdate(bool force, const FAR_FIND_DATA_EX &SrcData, const wchar_t *DestName) +{ + if (force || GetProcessUptimeMSec() - ProgressUpdateTime >= PROGRESS_REFRESH_THRESHOLD) + { + CP->SetProgressValue(CurCopiedSize, SrcData.nFileSize); + + if (ShowTotalCopySize) + { + CP->SetTotalProgressValue(TotalCopiedSize, TotalCopySize); + } + + CP->SetNames(SrcData.strFileName, DestName); + + ProgressUpdateTime = GetProcessUptimeMSec(); + } +} + + +/////////////////////////////////////////////////////////// BEGIN OF ShellFileTransfer + +ShellFileTransfer::ShellFileTransfer(const wchar_t *SrcName, const FAR_FIND_DATA_EX &SrcData, + const FARString &strDestName, bool Append, + ShellCopyBuffer &CopyBuffer, COPY_FLAGS &Flags) + : + _SrcName(SrcName), + _strDestName(strDestName), + _CopyBuffer(CopyBuffer), + _Flags(Flags), + _SrcData(SrcData) +{ + if (!_SrcFile.Open(SrcName, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN)) + throw ErrnoSaver(); + + if (_Flags.COPYXATTR) + { + _XAttrCopyPtr.reset(new ShellCopyFileExtendedAttributes(_SrcFile)); + } + + if (_Flags.COPYACCESSMODE) + { // force S_IWUSR for a while file being copied, it will be removed afterwards if not needed + _ModeToCreateWith = _SrcData.dwUnixMode | S_IWUSR; + } + + _DstFlags = FILE_FLAG_SEQUENTIAL_SCAN; + if (Flags.WRITETHROUGH) + { + _DstFlags|= FILE_FLAG_WRITE_THROUGH; + +#ifdef __linux__ //anyway OSX doesn't have O_DIRECT + if (SrcData.nFileSize > 32 * USE_PAGE_SIZE)// just empiric + _DstFlags|= FILE_FLAG_NO_BUFFERING; +#endif + } + + bool DstOpened = _DestFile.Open(_strDestName, GENERIC_WRITE, FILE_SHARE_READ, + _Flags.COPYACCESSMODE ? &_ModeToCreateWith : nullptr, + (Append ? OPEN_EXISTING : CREATE_ALWAYS), _DstFlags); + + if ((_DstFlags & (FILE_FLAG_WRITE_THROUGH|FILE_FLAG_NO_BUFFERING)) != 0) + { + if (!DstOpened) + { + _DstFlags&= ~(FILE_FLAG_WRITE_THROUGH|FILE_FLAG_NO_BUFFERING); + DstOpened = _DestFile.Open(_strDestName, GENERIC_WRITE, FILE_SHARE_READ, + _Flags.COPYACCESSMODE ? &_ModeToCreateWith : nullptr, + (Append ? OPEN_EXISTING : CREATE_ALWAYS), _DstFlags); + if (DstOpened) + { + Flags.WRITETHROUGH = false; + fprintf(stderr, "COPY: unbuffered FAILED: 0x%x\n", + _DstFlags & (FILE_FLAG_WRITE_THROUGH|FILE_FLAG_NO_BUFFERING)); + } + }/* else + fprintf(stderr, "COPY: unbuffered OK: 0x%x\n", DstFlags & (FILE_FLAG_WRITE_THROUGH|FILE_FLAG_NO_BUFFERING));*/ + } + + if (!DstOpened) + { + ErrnoSaver ErSr; + _SrcFile.Close(); + _LOGCOPYR(SysLog(L"return COPY_FAILURE -> %d CreateFile=-1, LastError=%d (0x%08X)",__LINE__,_localLastError,_localLastError)); + throw ErSr; + } + + if (Append) + { + _AppendPos = 0; + if (!_DestFile.SetPointer(0, &_AppendPos, FILE_END)) + { + ErrnoSaver ErSr; + _SrcFile.Close(); + _DestFile.SetEnd(); + _DestFile.Close(); + throw ErSr; + } + } + else if (SrcData.nFileSize > (uint64_t)_CopyBuffer.Size && !_Flags.SPARSEFILES && !_Flags.USECOW) + { + _DestFile.AllocationHint(SrcData.nFileSize); + } +} + +ShellFileTransfer::~ShellFileTransfer() +{ + if (!_Done) try + { + fprintf(stderr, "~ShellFileTransfer: discarding '%ls'\n", _strDestName.CPtr()); + _SrcFile.Close(); + CP->SetProgressValue(0,0); + CurCopiedSize = 0; // Сбросить текущий прогресс + + if (_AppendPos != -1) + { + _DestFile.SetPointer(_AppendPos, nullptr, FILE_BEGIN); + } + + _DestFile.SetEnd(); + _DestFile.Close(); + + if (_AppendPos == -1) + { + TemporaryMakeWritable tmw(_strDestName); + apiDeleteFile(_strDestName); + } + + ProgressUpdate(true, _SrcData, _strDestName); + } + catch (std::exception &ex) + { + fprintf(stderr, "~ShellFileTransfer: %s\n", ex.what()); + } + catch (...) + { + fprintf(stderr, "~ShellFileTransfer: ...\n"); + } +} + +void ShellFileTransfer::Do() +{ + CP->SetProgressValue(0,0); + + for (;;) + { + ProgressUpdate(false, _SrcData, _strDestName); + + BOOL IsChangeConsole = OrigScrX != ScrX || OrigScrY != ScrY; + + IsChangeConsole = CheckAndUpdateConsole(IsChangeConsole); + + if (IsChangeConsole) + { + OrigScrX = ScrX; + OrigScrY = ScrY; + PR_ShellCopyMsg(); + } + + if (CP->Cancelled()) + return; + + _Stopwatch = (_SrcData.nFileSize - CurCopiedSize > (uint64_t)_CopyBuffer.Size) ? GetProcessUptimeMSec() : 0; + + DWORD BytesWritten = PieceCopy(); + if (BytesWritten == 0) + break; + + CurCopiedSize+= BytesWritten; + + if (_Stopwatch != 0 && BytesWritten == _CopyBuffer.Size) + { + _Stopwatch = GetProcessUptimeMSec() - _Stopwatch; + if (_Stopwatch < 100) + { + if (_CopyBuffer.Size < _CopyBuffer.Capacity) + { + _CopyBuffer.Size = std::min(_CopyBuffer.Size * 2, _CopyBuffer.Capacity); + fprintf(stderr, "CopyPieceSize increased to %d\n", _CopyBuffer.Size); + } + } + else if (_Stopwatch >= 1000 && _CopyBuffer.Size > (int)COPY_PIECE_MINIMAL) + { + _CopyBuffer.Size = std::max(_CopyBuffer.Size / 2, (DWORD)COPY_PIECE_MINIMAL); + fprintf(stderr, "CopyPieceSize decreased to %d\n", _CopyBuffer.Size); + } + } + + if (ShowTotalCopySize) + TotalCopiedSize+=BytesWritten; + } + + _SrcFile.Close(); + + if (!apiIsDevNull(_strDestName)) // avoid sudo prompt when copying to /dev/null + { + if (_LastWriteWasHole) + { + while (!_DestFile.SetEnd()) + { + RetryCancel(MSG(MCopyWriteError), _strDestName); + } + } + + if (_XAttrCopyPtr) + _XAttrCopyPtr->ApplyToCopied(_DestFile); + + if (_Flags.COPYACCESSMODE && _ModeToCreateWith != _SrcData.dwUnixMode) + _DestFile.Chmod(_SrcData.dwUnixMode); + + _DestFile.SetTime(nullptr, nullptr, &_SrcData.ftLastWriteTime, nullptr); + } + + _DestFile.Close(); + + _Done = true; + + ProgressUpdate(false, _SrcData, _strDestName); +} + +void ShellFileTransfer::RetryCancel(const wchar_t *Text, const wchar_t *Object) +{ + ErrnoSaver ErSr; + _Stopwatch = 0; // UI messes timings + const int MsgCode = Message(MSG_WARNING | MSG_ERRORTYPE, 2, + MSG(MError), Text, Object, MSG(MRetry), MSG(MCancel)); + + PR_ShellCopyMsg(); + + if (MsgCode != 0) + throw ErSr; +} + +// returns std:::pair<OffsetOfNextHole, SizeOfNextHole> (SizeOfNextHole==0 means no holes found) +static std::pair<DWORD, DWORD> LookupNextHole(const unsigned char *Data, DWORD Size, uint64_t Offset) +{ + const DWORD Alignment = 0x1000; // must be power of 2 + const DWORD OffsetMisalignment = DWORD(Offset) & (Alignment - 1); + + DWORD i = 0; + if (OffsetMisalignment) { + i+= Alignment - OffsetMisalignment; + } + + for (; i < Size; i+= Alignment) + { + DWORD ZeroesCount = 0; + while (i + ZeroesCount < Size && Data[i + ZeroesCount] == 0) + { + ++ZeroesCount; + } + if (ZeroesCount >= Alignment) + { + return std::make_pair(i, (ZeroesCount / Alignment) * Alignment); + } + } + + return std::make_pair(Size, (DWORD)0); +} + +DWORD ShellFileTransfer::PieceCopy() +{ +#if defined(COW_SUPPORTED) && defined(__linux__) + if (_Flags.USECOW) for(;;) + { + ssize_t sz = copy_file_range(_SrcFile.Descriptor(), + nullptr, _DestFile.Descriptor(), nullptr, _CopyBuffer.Size, 0); + + if (sz >= 0) + return (DWORD)sz; + + if (errno == EXDEV) + { + fprintf(stderr, "copy_file_range returned EXDEV, fallback to usual copy\n"); + break; + } + + RetryCancel(MSG(MCopyWriteError), _strDestName); + } +#endif + + DWORD BytesRead, BytesWritten; + + while (!_SrcFile.Read(_CopyBuffer.Ptr, _CopyBuffer.Size, &BytesRead)) + { + RetryCancel(MSG(MCopyReadError), _SrcName); + } + + if (BytesRead == 0) + return BytesRead; + + DWORD WriteSize = BytesRead; + if ((_DstFlags & FILE_FLAG_NO_BUFFERING) != 0) + WriteSize = AlignPageUp(WriteSize); + + BytesWritten = 0; + if (_Flags.SPARSEFILES) + { + while (BytesWritten < WriteSize) + { + const unsigned char *Data = (const unsigned char *)_CopyBuffer.Ptr + BytesWritten; + const std::pair<DWORD, DWORD> &NH = + LookupNextHole(Data, WriteSize - BytesWritten, CurCopiedSize + BytesWritten); + DWORD LeadingNonzeroesWritten = NH.first ? PieceWrite(Data, NH.first) : 0; + BytesWritten+= LeadingNonzeroesWritten; + if (NH.second && LeadingNonzeroesWritten == NH.first) + { + // fprintf(stderr, "!!! HOLE of size %x\n", SR.second); + BytesWritten+= PieceWriteHole(NH.second); + } + } + } + else + BytesWritten = PieceWrite(_CopyBuffer.Ptr, WriteSize); + + if (BytesWritten > BytesRead) + { // likely we written bit more due to no_buffering requires aligned io + // move backward and correct file size + _DestFile.SetPointer((INT64)BytesRead - (INT64)WriteSize, nullptr, FILE_CURRENT); + _DestFile.SetEnd(); + return BytesRead; + } + + if (BytesWritten < BytesRead) + { // if written less than read then need to rewind source file by difference + _SrcFile.SetPointer((INT64)BytesWritten - (INT64)BytesRead, nullptr, FILE_CURRENT); + } + + return BytesWritten; +} + +DWORD ShellFileTransfer::PieceWriteHole(DWORD Size) +{ + while (!_DestFile.SetPointer(Size, nullptr, FILE_CURRENT)) + { + RetryCancel(MSG(MCopyWriteError), _strDestName); + } + _LastWriteWasHole = true; + return Size; +} + +DWORD ShellFileTransfer::PieceWrite(const void *Data, DWORD Size) +{ + DWORD BytesWritten = 0; + while (!_DestFile.Write(Data, Size, &BytesWritten)) + { + RetryCancel(MSG(MCopyWriteError), _strDestName); + } + _LastWriteWasHole = false; + return BytesWritten; +} + +/////////////////////////////////////////////////////////// END OF ShellFileTransfer + +int ShellCopy::ShellCopyFile(const wchar_t *SrcName,const FAR_FIND_DATA_EX &SrcData, + FARString &strDestName,int Append) +{ + OrigScrX = ScrX; + OrigScrY = ScrY; + + if (Flags.LINK) + { + if (RPT==RP_HARDLINK) + { + apiDeleteFile(strDestName); //BUGBUG + return (MkHardLink(SrcName,strDestName) ? COPY_SUCCESS:COPY_FAILURE); + } + else + { + return (MkSymLink(SrcName,strDestName,RPT,true) ? COPY_SUCCESS:COPY_FAILURE); + } + } + + try + { +#if defined(COW_SUPPORTED) && defined(__APPLE__) + if (Flags.USECOW) + { + const std::string mbSrc = Wide2MB(SrcName); + const std::string &mbDest = strDestName.GetMB(); + int r = clonefile(mbSrc.c_str(), mbDest.c_str(), 0); + if (r == 0) + { + // fprintf(stderr, "CoW succeeded for '%s' -> '%s'\n", mbSrc.c_str(), mbDest.c_str()); + CurCopiedSize = SrcData.nFileSize; + if (ShowTotalCopySize) + TotalCopiedSize+= SrcData.nFileSize; + + ProgressUpdate(false, SrcData, strDestName); + return CP->Cancelled() ? COPY_CANCEL : COPY_SUCCESS; + } + + ErrnoSaver ErSr; + if (ErSr.Get() != EXDEV && ErSr.Get() != ENOTSUP) + throw ErSr; + + fprintf(stderr, "Skip CoW errno=%d for '%s' -> '%s'\n", ErSr.Get(), mbSrc.c_str(), mbDest.c_str()); + } +#endif + + + ShellFileTransfer(SrcName, SrcData, strDestName, Append != 0, CopyBuffer, Flags).Do(); + return CP->Cancelled() ? COPY_CANCEL : COPY_SUCCESS; + } + catch (ErrnoSaver &ErSr) + { + _localLastError = ErSr.Get(); + } + + return CP->Cancelled() ? COPY_CANCEL : COPY_FAILURE; +} + +void ShellCopy::SetDestDizPath(const wchar_t *DestPath) +{ + if (!Flags.DIZREAD) + { + ConvertNameToFull(DestPath, strDestDizPath); + CutToSlash(strDestDizPath); + + if (strDestDizPath.IsEmpty()) + strDestDizPath = L"."; + + if ((Opt.Diz.UpdateMode==DIZ_UPDATE_IF_DISPLAYED && !SrcPanel->IsDizDisplayed()) || + Opt.Diz.UpdateMode==DIZ_NOT_UPDATE) + strDestDizPath.Clear(); + + if (!strDestDizPath.IsEmpty()) + DestDiz.Read(strDestDizPath); + + Flags.DIZREAD = true; + } +} + +enum WarnDlgItems +{ + WDLG_BORDER, + WDLG_TEXT, + WDLG_FILENAME, + WDLG_SEPARATOR, + WDLG_SRCFILEBTN, + WDLG_DSTFILEBTN, + WDLG_SEPARATOR2, + WDLG_CHECKBOX, + WDLG_SEPARATOR3, + WDLG_OVERWRITE, + WDLG_SKIP, + WDLG_RENAME, + WDLG_APPEND, + WDLG_CANCEL, +}; + +#define DM_OPENVIEWER DM_USER+33 + +LONG_PTR WINAPI WarnDlgProc(HANDLE hDlg,int Msg,int Param1,LONG_PTR Param2) +{ + switch (Msg) + { + case DM_OPENVIEWER: + { + LPCWSTR ViewName=nullptr; + FARString** WFN=reinterpret_cast<FARString**>(SendDlgMessage(hDlg,DM_GETDLGDATA,0,0)); + + if (WFN) + { + switch (Param1) + { + case WDLG_SRCFILEBTN: + ViewName=*WFN[0]; + break; + case WDLG_DSTFILEBTN: + ViewName=*WFN[1]; + break; + } + + FileViewer Viewer(ViewName,FALSE,FALSE,TRUE,-1,nullptr,nullptr,FALSE); + Viewer.SetDynamicallyBorn(FALSE); + // а этот трюк не даст пользователю сменить текущий каталог по CtrlF10 и этим ввести в заблуждение копир: + Viewer.SetTempViewName(L"nul",FALSE); + FrameManager->EnterModalEV(); + FrameManager->ExecuteModal(); + FrameManager->ExitModalEV(); + FrameManager->ProcessKey(KEY_CONSOLE_BUFFER_RESIZE); + } + } + break; + case DN_CTLCOLORDLGITEM: + { + if (Param1==WDLG_FILENAME) + { + int Color=FarColorToReal(COL_WARNDIALOGTEXT)&0xFF; + return ((Param2&0xFF00FF00)|(Color<<16)|Color); + } + } + break; + case DN_BTNCLICK: + { + switch (Param1) + { + case WDLG_SRCFILEBTN: + case WDLG_DSTFILEBTN: + SendDlgMessage(hDlg,DM_OPENVIEWER,Param1,0); + break; + case WDLG_RENAME: + { + FARString** WFN=reinterpret_cast<FARString**>(SendDlgMessage(hDlg,DM_GETDLGDATA,0,0)); + FARString strDestName=*WFN[1]; + GenerateName(strDestName,*WFN[2]); + + if (SendDlgMessage(hDlg,DM_GETCHECK,WDLG_CHECKBOX,0)==BSTATE_UNCHECKED) + { + int All=BSTATE_UNCHECKED; + + if (GetString(MSG(MCopyRenameTitle),MSG(MCopyRenameText),nullptr,strDestName,*WFN[1],L"CopyAskOverwrite",FIB_BUTTONS|FIB_NOAMPERSAND|FIB_EXPANDENV|FIB_CHECKBOX,&All,MSG(MCopyRememberChoice))) + { + if (All!=BSTATE_UNCHECKED) + { + *WFN[2]=*WFN[1]; + CutToSlash(*WFN[2]); + } + + SendDlgMessage(hDlg,DM_SETCHECK,WDLG_CHECKBOX,All); + } + else + { + return TRUE; + } + } + else + { + *WFN[1]=strDestName; + } + } + break; + } + } + break; + case DN_KEY: + { + if ((Param1==WDLG_SRCFILEBTN || Param1==WDLG_DSTFILEBTN) && Param2==KEY_F3) + { + SendDlgMessage(hDlg,DM_OPENVIEWER,Param1,0); + } + } + break; + } + + return DefDlgProc(hDlg,Msg,Param1,Param2); +} + +int ShellCopy::AskOverwrite(const FAR_FIND_DATA_EX &SrcData, + const wchar_t *SrcName, + const wchar_t *DestName, DWORD DestAttr, + int SameName,int Rename,int AskAppend, + int &Append,FARString &strNewName,int &RetCode) +{ + enum + { + WARN_DLG_HEIGHT=13, + WARN_DLG_WIDTH=72, + }; + DialogDataEx WarnCopyDlgData[]= + { + {DI_DOUBLEBOX,3,1,WARN_DLG_WIDTH-4,WARN_DLG_HEIGHT-2,{},0,MSG(MWarning)}, + {DI_TEXT,5,2,WARN_DLG_WIDTH-6,2,{},DIF_CENTERTEXT,MSG(MCopyFileExist)}, + {DI_EDIT,5,3,WARN_DLG_WIDTH-6,3,{},DIF_READONLY,(wchar_t*)DestName}, + {DI_TEXT,3,4,0,4,{},DIF_SEPARATOR,L""}, + {DI_BUTTON,5,5,WARN_DLG_WIDTH-6,5,{},DIF_BTNNOCLOSE|DIF_NOBRACKETS,L""}, + {DI_BUTTON,5,6,WARN_DLG_WIDTH-6,6,{},DIF_BTNNOCLOSE|DIF_NOBRACKETS,L""}, + {DI_TEXT,3,7,0,7,{},DIF_SEPARATOR,L""}, + {DI_CHECKBOX,5,8,0,8,{},DIF_FOCUS,MSG(MCopyRememberChoice)}, + {DI_TEXT,3,9,0,9,{},DIF_SEPARATOR,L""}, + + {DI_BUTTON,0,10,0,10,{},DIF_DEFAULT|DIF_CENTERGROUP,MSG(MCopyOverwrite)}, + {DI_BUTTON,0,10,0,10,{},DIF_CENTERGROUP,MSG(MCopySkipOvr)}, + {DI_BUTTON,0,10,0,10,{},DIF_CENTERGROUP,MSG(MCopyRename)}, + {DI_BUTTON,0,10,0,10,{},DIF_CENTERGROUP|(AskAppend?0:(DIF_DISABLE|DIF_HIDDEN)),MSG(MCopyAppend)}, + {DI_BUTTON,0,10,0,10,{},DIF_CENTERGROUP,MSG(MCopyCancelOvr)} + }; + FAR_FIND_DATA_EX DestData; + DestData.Clear(); + int DestDataFilled=FALSE; + Append=FALSE; + + if (DestAttr==INVALID_FILE_ATTRIBUTES) + if ((DestAttr=apiGetFileAttributes(DestName))==INVALID_FILE_ATTRIBUTES) + return TRUE; + + if (DestAttr & FILE_ATTRIBUTE_DIRECTORY) + return TRUE; + + int MsgCode=OvrMode; + FARString strDestName=DestName; + + if (OvrMode==-1) + { + int Type; + + if ((!Opt.Confirm.Copy && !Rename) || (!Opt.Confirm.Move && Rename) || + SameName || (Type=apiGetFileTypeByName(DestName))==FILE_TYPE_CHAR || + Type==FILE_TYPE_PIPE || Flags.OVERWRITENEXT) + MsgCode=1; + else + { + DestData.Clear(); + apiGetFindDataForExactPathName(DestName,DestData); + DestDataFilled=TRUE; + + if (Flags.ONLYNEWERFILES) + { + // сравним время + int64_t RetCompare=FileTimeDifference(&DestData.ftLastWriteTime,&SrcData.ftLastWriteTime); + + if (RetCompare < 0) + MsgCode=0; + else + MsgCode=2; + } + else + { + FormatString strSrcFileStr, strDestFileStr; + uint64_t SrcSize = SrcData.nFileSize; + FILETIME SrcLastWriteTime = SrcData.ftLastWriteTime; + if (Flags.SYMLINK == COPY_SYMLINK_ASFILE && (SrcData.dwFileAttributes&(FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_BROKEN)) == FILE_ATTRIBUTE_REPARSE_POINT) + { + FARString RealName = SrcName; + FAR_FIND_DATA_EX FindData; + apiGetFindDataForExactPathName(RealName,FindData); + SrcSize=FindData.nFileSize; + SrcLastWriteTime = FindData.ftLastWriteTime; + } + FormatString strSrcSizeText; + strSrcSizeText<<SrcSize; + uint64_t DestSize = DestData.nFileSize; + FormatString strDestSizeText; + strDestSizeText<<DestSize; + FARString strDateText, strTimeText; + ConvertDate(SrcLastWriteTime,strDateText,strTimeText,8,FALSE,FALSE,TRUE,TRUE); + strSrcFileStr<<fmt::LeftAlign()<<fmt::Width(17)<<MSG(MCopySource)<<L" "<<fmt::Width(25)<<fmt::Precision(25)<<strSrcSizeText<<L" "<<strDateText<<L" "<<strTimeText; + ConvertDate(DestData.ftLastWriteTime,strDateText,strTimeText,8,FALSE,FALSE,TRUE,TRUE); + strDestFileStr<<fmt::LeftAlign()<<fmt::Width(17)<<MSG(MCopyDest)<<L" "<<fmt::Width(25)<<fmt::Precision(25)<<strDestSizeText<<L" "<<strDateText<<L" "<<strTimeText; + + WarnCopyDlgData[WDLG_SRCFILEBTN].Data=strSrcFileStr; + WarnCopyDlgData[WDLG_DSTFILEBTN].Data=strDestFileStr; + MakeDialogItemsEx(WarnCopyDlgData,WarnCopyDlg); + FARString strFullSrcName; + ConvertNameToFull(SrcName,strFullSrcName); + FARString *WFN[]={&strFullSrcName,&strDestName,&strRenamedFilesPath}; + Dialog WarnDlg(WarnCopyDlg,ARRAYSIZE(WarnCopyDlg),WarnDlgProc,(LONG_PTR)&WFN); + WarnDlg.SetDialogMode(DMODE_WARNINGSTYLE); + WarnDlg.SetPosition(-1,-1,WARN_DLG_WIDTH,WARN_DLG_HEIGHT); + WarnDlg.SetHelp(L"CopyAskOverwrite"); + WarnDlg.SetId(CopyOverwriteId); + WarnDlg.Process(); + + switch (WarnDlg.GetExitCode()) + { + case WDLG_OVERWRITE: + MsgCode=WarnCopyDlg[WDLG_CHECKBOX].Selected?1:0; + break; + case WDLG_SKIP: + MsgCode=WarnCopyDlg[WDLG_CHECKBOX].Selected?3:2; + break; + case WDLG_RENAME: + MsgCode=WarnCopyDlg[WDLG_CHECKBOX].Selected?5:4; + break; + case WDLG_APPEND: + MsgCode=WarnCopyDlg[WDLG_CHECKBOX].Selected?7:6; + break; + case -1: + case -2: + case WDLG_CANCEL: + MsgCode=8; + break; + } + } + } + } + + switch (MsgCode) + { + case 1: + OvrMode=1; + case 0: + break; + case 3: + OvrMode=2; + case 2: + RetCode=COPY_NEXT; + return FALSE; + case 5: + OvrMode=5; + GenerateName(strDestName,strRenamedFilesPath); + case 4: + RetCode=COPY_RETRY; + strNewName=strDestName; + break; + case 7: + OvrMode=6; + case 6: + Append=TRUE; + break; + case -1: + case -2: + case 8: + RetCode=COPY_CANCEL; + return FALSE; + } + + if (RetCode!=COPY_RETRY) + { + if ((DestAttr & FILE_ATTRIBUTE_READONLY) && !Flags.OVERWRITENEXT) + { + int MsgCode=0; + + if (!SameName) + { + if (ReadOnlyOvrMode!=-1) + { + MsgCode=ReadOnlyOvrMode; + } + else + { + if (!DestDataFilled) + { + DestData.Clear(); + apiGetFindDataForExactPathName(DestName,DestData); + } + + FARString strDateText,strTimeText; + FormatString strSrcFileStr, strDestFileStr; + uint64_t SrcSize = SrcData.nFileSize; + FormatString strSrcSizeText; + strSrcSizeText<<SrcSize; + uint64_t DestSize = DestData.nFileSize; + FormatString strDestSizeText; + strDestSizeText<<DestSize; + ConvertDate(SrcData.ftLastWriteTime,strDateText,strTimeText,8,FALSE,FALSE,TRUE,TRUE); + strSrcFileStr<<fmt::LeftAlign()<<fmt::Width(17)<<MSG(MCopySource)<<L" "<<fmt::Width(25)<<fmt::Precision(25)<<strSrcSizeText<<L" "<<strDateText<<L" "<<strTimeText; + ConvertDate(DestData.ftLastWriteTime,strDateText,strTimeText,8,FALSE,FALSE,TRUE,TRUE); + strDestFileStr<<fmt::LeftAlign()<<fmt::Width(17)<<MSG(MCopyDest)<<L" "<<fmt::Width(25)<<fmt::Precision(25)<<strDestSizeText<<L" "<<strDateText<<L" "<<strTimeText; + WarnCopyDlgData[WDLG_SRCFILEBTN].Data=strSrcFileStr; + WarnCopyDlgData[WDLG_DSTFILEBTN].Data=strDestFileStr; + WarnCopyDlgData[WDLG_TEXT].Data=MSG(MCopyFileRO); + WarnCopyDlgData[WDLG_OVERWRITE].Data=MSG(Append?MCopyAppend:MCopyOverwrite); + WarnCopyDlgData[WDLG_RENAME].Type=DI_TEXT; + WarnCopyDlgData[WDLG_RENAME].Data=L""; + WarnCopyDlgData[WDLG_APPEND].Type=DI_TEXT; + WarnCopyDlgData[WDLG_APPEND].Data=L""; + MakeDialogItemsEx(WarnCopyDlgData,WarnCopyDlg); + FARString strSrcName; + ConvertNameToFull(SrcData.strFileName,strSrcName); + LPCWSTR WFN[2]={strSrcName,DestName}; + Dialog WarnDlg(WarnCopyDlg,ARRAYSIZE(WarnCopyDlg),WarnDlgProc,(LONG_PTR)&WFN); + WarnDlg.SetDialogMode(DMODE_WARNINGSTYLE); + WarnDlg.SetPosition(-1,-1,WARN_DLG_WIDTH,WARN_DLG_HEIGHT); + WarnDlg.SetHelp(L"CopyFiles"); + WarnDlg.SetId(CopyReadOnlyId); + WarnDlg.Process(); + + switch (WarnDlg.GetExitCode()) + { + case WDLG_OVERWRITE: + MsgCode=WarnCopyDlg[WDLG_CHECKBOX].Selected?1:0; + break; + case WDLG_SKIP: + MsgCode=WarnCopyDlg[WDLG_CHECKBOX].Selected?3:2; + break; + case -1: + case -2: + case WDLG_CANCEL: + MsgCode=8; + break; + } + } + } + + switch (MsgCode) + { + case 1: + ReadOnlyOvrMode=1; + case 0: + break; + case 3: + ReadOnlyOvrMode=2; + case 2: + RetCode=COPY_NEXT; + return FALSE; + case -1: + case -2: + case 8: + RetCode=COPY_CANCEL; + return FALSE; + } + } + + if (!SameName && (DestAttr & (FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM))) + apiMakeWritable(DestName); + } + + return TRUE; +} + + + + +BOOL ShellCopySecuryMsg(const wchar_t *Name) +{ + static clock_t PrepareSecuryStartTime; + + if (!Name || !*Name || (static_cast<DWORD>(GetProcessUptimeMSec() - PrepareSecuryStartTime) > Opt.ShowTimeoutDACLFiles)) + { + static int Width=30; + int WidthTemp; + if (Name && *Name) + { + PrepareSecuryStartTime = GetProcessUptimeMSec(); // Первый файл рисуется всегда + WidthTemp=Max(StrLength(Name),30); + } + else + Width=WidthTemp=30; + + // ширина месага - 38% + WidthTemp=Min(WidthTemp,WidthNameForMessage); + Width=Max(Width,WidthTemp); + + FARString strOutFileName = Name; //??? nullptr ??? + TruncPathStr(strOutFileName,Width); + CenterStr(strOutFileName, strOutFileName,Width+4); + Message(0,0,MSG(MMoveDlgTitle),MSG(MCopyPrepareSecury),strOutFileName); + + if (CP->Cancelled()) + { + return FALSE; + } + } + + PreRedrawItem preRedrawItem=PreRedraw.Peek(); + preRedrawItem.Param.Param1=Name; + PreRedraw.SetParam(preRedrawItem.Param); + return TRUE; +} + + +bool ShellCopy::CalcTotalSize() +{ + FARString strSelName; + DWORD FileAttr; + uint64_t FileSize; + // Для фильтра + FAR_FIND_DATA_EX fd; + PreRedraw.Push(PR_ShellCopyMsg); + PreRedrawItem preRedrawItem=PreRedraw.Peek(); + preRedrawItem.Param.Param1=CP; + PreRedraw.SetParam(preRedrawItem.Param); + TotalCopySize=CurCopiedSize=0; + TotalFilesToProcess = 0; + SrcPanel->GetSelNameCompat(nullptr,FileAttr); + + while (SrcPanel->GetSelNameCompat(&strSelName,FileAttr,&fd)) + { + if ((FileAttr&FILE_ATTRIBUTE_REPARSE_POINT) && Flags.SYMLINK != COPY_SYMLINK_ASFILE) + continue; + + if (FileAttr & FILE_ATTRIBUTE_DIRECTORY) + { + { + uint32_t DirCount,FileCount,ClusterSize; + uint64_t PhysicalSize; + CP->SetScanName(strSelName); + int __Ret=GetDirInfo(L"",strSelName,DirCount,FileCount,FileSize,PhysicalSize,ClusterSize,-1,Filter, + ((Flags.SYMLINK == COPY_SYMLINK_ASFILE)?GETDIRINFO_SCANSYMLINK:0)| + (UseFilter?GETDIRINFO_USEFILTER:0)); + + if (__Ret <= 0) + { + ShowTotalCopySize=false; + PreRedraw.Pop(); + return FALSE; + } + + if (FileCount > 0) + { + TotalCopySize+=FileSize; + TotalFilesToProcess += FileCount; + } + } + } + else + { + // Подсчитаем количество файлов + if (UseFilter) + { + if (!Filter->FileInFilter(fd)) + continue; + } + + FileSize = SrcPanel->GetLastSelectedSize(); + + if (FileSize != (uint64_t)-1) + { + TotalCopySize+=FileSize; + TotalFilesToProcess++; + } + } + } + + // INFO: Это для варианта, когда "ВСЕГО = общий размер * количество целей" + TotalCopySize=TotalCopySize*CountTarget; + InsertCommas(TotalCopySize,strTotalCopySizeText); + PreRedraw.Pop(); + return true; +} |