7 Copyright (c) 1996 Eugene Roshal
8 Copyright (c) 2000 Far Group
11 Redistribution and use in source and binary forms, with or without
12 modification, are permitted provided that the following conditions
14 1. Redistributions of source code must retain the above copyright
15 notice, this list of conditions and the following disclaimer.
16 2. Redistributions in binary form must reproduce the above copyright
17 notice, this list of conditions and the following disclaimer in the
18 documentation and/or other materials provided with the distribution.
19 3. The name of the authors may not be used to endorse or promote products
20 derived from this software without specific prior written permission.
22 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #include "headers.hpp"
41 #include "ctrlobj.hpp"
42 #include "filepanels.hpp"
44 #include "filelist.hpp"
45 #include "foldtree.hpp"
46 #include "treelist.hpp"
47 #include "chgprior.hpp"
48 #include "scantree.hpp"
49 #include "constitle.hpp"
50 #include "filefilter.hpp"
51 #include "fileview.hpp"
52 #include "TPreRedrawFunc.hpp"
56 #include "keyboard.hpp"
57 #include "palette.hpp"
58 #include "message.hpp"
61 #include "fileattr.hpp"
62 #include "datetime.hpp"
63 #include "dirinfo.hpp"
64 #include "pathmix.hpp"
65 #include "drivemix.hpp"
68 #include "panelmix.hpp"
69 #include "processname.hpp"
71 #include "DlgGuid.hpp"
72 #include "console.hpp"
73 #include "wakeful.hpp"
77 #if defined(__APPLE__)
78 #include <AvailabilityMacros.h>
79 #if defined(MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12
81 #include <sys/clonefile.h>
85 #elif defined(__linux__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 27))
90 /* Общее время ожидания пользователя */
91 extern long WaitUserTime
;
92 /* Для того, что бы время при ожидании пользователя тикало, а remaining/speed нет */
93 static long OldCalcTime
;
95 #define PROGRESS_REFRESH_THRESHOLD 200 // msec
99 COPY_BUFFER_SIZE
= 0x800000,
100 COPY_PIECE_MINIMAL
= 0x10000
105 COPY_RULE_NUL
= 0x0001,
106 COPY_RULE_FILES
= 0x0002,
109 static int TotalFiles
, TotalFilesToProcess
;
111 static clock_t CopyStartTime
;
113 static int OrigScrX
, OrigScrY
;
115 static uint64_t TotalCopySize
, TotalCopiedSize
; // Общий индикатор копирования
116 static uint64_t CurCopiedSize
; // Текущий индикатор копирования
117 static uint64_t TotalSkippedSize
; // Общий размер пропущенных файлов
118 static size_t CountTarget
; // всего целей.
119 static bool ShowTotalCopySize
;
120 static FARString strTotalCopySizeText
;
122 static FileFilter
*Filter
;
123 static int UseFilter
= FALSE
;
125 static clock_t ProgressUpdateTime
; // Last progress bar update time
127 ShellCopyFileExtendedAttributes::ShellCopyFileExtendedAttributes(File
&f
)
129 _apply
= (f
.QueryFileExtendedAttributes(_xattr
) != FB_NO
&& !_xattr
.empty());
132 void ShellCopyFileExtendedAttributes::ApplyToCopied(File
&f
)
135 f
.SetFileExtendedAttributes(_xattr
);
141 ShellCopy
*thisClass
;
147 FARString strPluginFormat
;
160 ID_SC_COPYACCESSMODE
,
165 ID_SC_COPYSYMLINK_TEXT
,
166 ID_SC_COPYSYMLINK_COMBO
,
167 ID_SC_COPYSYMLINK_EXPLAIN_TEXT
,
175 ID_SC_SOURCEFILENAME
,
190 // CopyProgress start
191 // гнать это отсюда в отдельный файл после разбора кучи глобальных переменных вверху
194 ConsoleTitle CopyTitle
;
199 bool Move
, Total
, Time
;
200 bool BgInit
, ScanBgInit
;
205 FARString strSrc
, strDst
, strFiles
, strTime
;
209 void CreateScanBackground();
210 void SetProgress(bool TotalProgress
, UINT64 CompletedSize
, UINT64 TotalSize
);
213 CopyProgress(bool Move
, bool Total
, bool Time
);
214 void CreateBackground();
215 bool Cancelled() { return IsCancelled
; }
216 void SetScanName(const wchar_t *Name
);
217 void SetNames(const wchar_t *Src
, const wchar_t *Dst
);
218 void SetProgressValue(UINT64 CompletedSize
, UINT64 TotalSize
)
220 return SetProgress(false, CompletedSize
, TotalSize
);
222 void SetTotalProgressValue(UINT64 CompletedSize
, UINT64 TotalSize
)
224 return SetProgress(true, CompletedSize
, TotalSize
);
228 static void GetTimeText(DWORD Time
, FARString
&strTimeText
)
231 DWORD Min
= Sec
/ 60;
233 DWORD Hour
= Min
/ 60;
235 strTimeText
.Format(L
"%02u:%02u:%02u", Hour
, Min
, Sec
);
238 bool CopyProgress::Timer()
241 DWORD Time
= GetProcessUptimeMSec();
243 if (!LastWriteTime
|| (Time
- LastWriteTime
>= RedrawTimeout
)) {
244 LastWriteTime
= Time
;
251 void CopyProgress::Flush()
255 if (CheckForEscSilent()) {
256 LockFrame
LF((*FrameManager
)[0]);
257 IsCancelled
= ConfirmAbortOp() != 0;
261 if (Total
|| (TotalFilesToProcess
== 1)) {
262 CopyTitle
.Set(L
"{%d%%} %ls",
263 Total
? ToPercent64(TotalCopiedSize
>> 8, TotalCopySize
>> 8) : Percents
,
264 (Move
? Msg::CopyMovingTitle
: Msg::CopyCopyingTitle
).CPtr());
269 CopyProgress::CopyProgress(bool Move
, bool Total
, bool Time
)
278 Color(FarColorToReal(COL_DIALOGTEXT
)),
283 void CopyProgress::SetScanName(const wchar_t *Name
)
286 CreateScanBackground();
289 GotoXY(Rect
.Left
+ 5, Rect
.Top
+ 3);
290 FS
<< fmt::Cells() << fmt::LeftAlign() << fmt::Size(Rect
.Right
- Rect
.Left
- 9) << Name
;
294 void CopyProgress::CreateScanBackground()
296 for (size_t i
= 0; i
< BarSize
; i
++) {
301 Message(MSG_LEFTALIGN
, 0, (Move
? Msg::MoveDlgTitle
: Msg::CopyDlgTitle
), Msg::CopyScanning
, Bar
);
302 int MX1
, MY1
, MX2
, MY2
;
303 GetMessagePosition(MX1
, MY1
, MX2
, MY2
);
311 void CopyProgress::CreateBackground()
313 for (size_t i
= 0; i
< BarSize
; i
++) {
321 Message(MSG_LEFTALIGN
, 0, (Move
? Msg::MoveDlgTitle
: Msg::CopyDlgTitle
),
322 (Move
? Msg::CopyMoving
: Msg::CopyCopying
), L
"", Msg::CopyTo
, L
"", Bar
, L
"\x1", L
"");
324 Message(MSG_LEFTALIGN
, 0, (Move
? Msg::MoveDlgTitle
: Msg::CopyDlgTitle
),
325 (Move
? Msg::CopyMoving
: Msg::CopyCopying
), L
"", Msg::CopyTo
, L
"", Bar
, L
"\x1", L
"",
329 FARString
strTotalSeparator(L
"\x1 ");
330 strTotalSeparator
+= Msg::CopyDlgTotal
;
331 strTotalSeparator
+= L
": ";
332 strTotalSeparator
+= strTotalCopySizeText
;
333 strTotalSeparator
+= L
" ";
336 Message(MSG_LEFTALIGN
, 0, (Move
? Msg::MoveDlgTitle
: Msg::CopyDlgTitle
),
337 (Move
? Msg::CopyMoving
: Msg::CopyCopying
), L
"", Msg::CopyTo
, L
"", Bar
,
338 strTotalSeparator
, Bar
, L
"\x1", L
"");
340 Message(MSG_LEFTALIGN
, 0, (Move
? Msg::MoveDlgTitle
: Msg::CopyDlgTitle
),
341 (Move
? Msg::CopyMoving
: Msg::CopyCopying
), L
"", Msg::CopyTo
, L
"", Bar
,
342 strTotalSeparator
, Bar
, L
"\x1", L
"", L
"\x1", L
"");
346 int MX1
, MY1
, MX2
, MY2
;
347 GetMessagePosition(MX1
, MY1
, MX2
, MY2
);
356 void CopyProgress::DrawNames()
358 Text(Rect
.Left
+ 5, Rect
.Top
+ 3, Color
, strSrc
);
359 Text(Rect
.Left
+ 5, Rect
.Top
+ 5, Color
, strDst
);
360 Text(Rect
.Left
+ 5, Rect
.Top
+ (Total
? 10 : 8), Color
, strFiles
);
363 void CopyProgress::SetNames(const wchar_t *Src
, const wchar_t *Dst
)
369 FormatString FString
;
370 FString
<< fmt::Cells() << fmt::LeftAlign() << fmt::Size(Rect
.Right
- Rect
.Left
- 9) << Src
;
371 strSrc
= std::move(FString
.strValue());
373 FString
<< fmt::Cells() << fmt::LeftAlign() << fmt::Size(Rect
.Right
- Rect
.Left
- 9) << Dst
;
374 strDst
= std::move(FString
.strValue());
377 strFiles
.Format(Msg::CopyProcessedTotal
, TotalFiles
, TotalFilesToProcess
);
379 strFiles
.Format(Msg::CopyProcessed
, TotalFiles
);
386 void CopyProgress::SetProgress(bool TotalProgress
, UINT64 CompletedSize
, UINT64 TotalSize
)
392 if (Total
== TotalProgress
) {
395 UINT64 OldCompletedSize
= CompletedSize
;
396 UINT64 OldTotalSize
= TotalSize
;
399 CompletedSize
= Min(CompletedSize
, TotalSize
);
400 COORD BarCoord
= {(SHORT
)(Rect
.Left
+ 5), (SHORT
)(Rect
.Top
+ (TotalProgress
? 8 : 6))};
401 size_t BarLength
= Rect
.Right
- Rect
.Left
- 9 - 5; //-5 для процентов
402 size_t Length
= TotalSize
403 ? static_cast<size_t>((TotalSize
< 1000000 ? CompletedSize
: CompletedSize
/ 100) * BarLength
404 / (TotalSize
< 1000000 ? TotalSize
: TotalSize
/ 100))
407 for (size_t i
= 0; i
< BarLength
; i
++) {
408 Bar
[i
] = BoxSymbols
[BS_X_B0
];
412 for (size_t i
= 0; i
< Length
; i
++) {
413 Bar
[i
] = BoxSymbols
[BS_X_DB
];
418 Percents
= ToPercent64(CompletedSize
, TotalSize
);
419 FormatString strPercents
;
420 strPercents
<< fmt::Expand(4) << Percents
<< L
"%";
421 Text(BarCoord
.X
, BarCoord
.Y
, Color
, Bar
);
422 Text(static_cast<int>(BarCoord
.X
+ BarLength
), BarCoord
.Y
, Color
, strPercents
);
424 if (Time
&& (!Total
|| TotalProgress
)) {
425 DWORD WorkTime
= GetProcessUptimeMSec() - CopyStartTime
;
426 UINT64 SizeLeft
= (OldTotalSize
> OldCompletedSize
) ? (OldTotalSize
- OldCompletedSize
) : 0;
427 long CalcTime
= OldCalcTime
;
429 if (WaitUserTime
!= -1) // -1 => находимся в процессе ожидания ответа юзера
431 OldCalcTime
= CalcTime
= WorkTime
- WaitUserTime
;
438 strTime
.Format(Msg::CopyTimeInfo
, L
" ", L
" ", L
" ");
441 OldCompletedSize
= OldCompletedSize
- TotalSkippedSize
;
444 UINT64 CPS
= CalcTime
? OldCompletedSize
/ CalcTime
: 0;
445 DWORD TimeLeft
= static_cast<DWORD
>(CPS
? SizeLeft
/ CPS
: 0);
447 FileSizeToStr(strSpeed
, CPS
, 8, COLUMN_FLOATSIZE
| COLUMN_COMMAS
);
448 FARString strWorkTimeStr
, strTimeLeftStr
;
449 GetTimeText(WorkTime
, strWorkTimeStr
);
450 GetTimeText(TimeLeft
, strTimeLeftStr
);
451 if (strSpeed
.At(0) == L
' ' && strSpeed
.At(strSpeed
.GetLength() - 1) >= L
'0'
452 && strSpeed
.At(strSpeed
.GetLength() - 1) <= L
'9') {
456 strTime
.Format(Msg::CopyTimeInfo
, strWorkTimeStr
.CPtr(), strTimeLeftStr
.CPtr(), strSpeed
.CPtr());
459 Text(Rect
.Left
+ 5, Rect
.Top
+ (Total
? 12 : 10), Color
, strTime
);
466 static CopyProgress
*CP
= nullptr;
470 + Всегда работаем с реальными _длинными_ именами, в результате чего
471 отлавливается ситуация, когда
472 Src="D:\Program Files\filename"
473 Dest="D:\PROGRA~1\filename"
474 ("D:\PROGRA~1" - короткое имя для "D:\Program Files")
475 считается, что имена тоже одинаковые, а раньше считалось,
476 что они разные (функция не знала, что и в первом, и во втором случае
478 ! Оптимизация - "велосипед" заменен на DeleteEndSlash
479 ! Убираем всю самодеятельность по проверке имен с разным
480 регистром из функции прочь, потому что это нужно делать только при
481 переименовании, а функция вызывается и при копировании тоже.
482 Теперь функция вернет 1, для случая имен src=path\filename,
483 dest=path\filename (раньше возвращала 2 - т.е. сигнал об ошибке).
486 static int CmpFullNames(const wchar_t *Src
, const wchar_t *Dest
)
488 FARString strSrcFullName
, strDestFullName
;
490 // получим полные пути с учетом символических связей
491 ConvertNameToFull(Src
, strSrcFullName
);
492 ConvertNameToFull(Dest
, strDestFullName
);
493 DeleteEndSlash(strSrcFullName
);
494 DeleteEndSlash(strDestFullName
);
496 return !StrCmp(strSrcFullName
, strDestFullName
);
499 static FARString
&GetParentFolder(const wchar_t *Src
, FARString
&strDest
)
502 CutToSlash(strDest
, true);
506 static int CmpFullPath(const wchar_t *Src
, const wchar_t *Dest
)
508 FARString strSrcFullName
, strDestFullName
;
510 GetParentFolder(Src
, strSrcFullName
);
511 GetParentFolder(Dest
, strDestFullName
);
512 DeleteEndSlash(strSrcFullName
);
513 DeleteEndSlash(strDestFullName
);
515 return !StrCmp(strSrcFullName
, strDestFullName
);
518 static void GenerateName(FARString
&strName
, const wchar_t *Path
= nullptr)
521 FARString strTmp
= Path
;
523 strTmp
+= PointToName(strName
);
527 FARString strExt
= PointToExt(strName
);
528 size_t NameLength
= strName
.GetLength() - strExt
.GetLength();
530 for (int i
= 1; apiPathExists(strName
); i
++) {
531 WCHAR Suffix
[20] = L
"_";
532 _itow(i
, Suffix
+ 1, 10);
533 strName
.Truncate(NameLength
);
539 void PR_ShellCopyMsg()
541 PreRedrawItem preRedrawItem
= PreRedraw
.Peek();
543 if (preRedrawItem
.Param
.Param1
) {
544 ((CopyProgress
*)preRedrawItem
.Param
.Param1
)->CreateBackground();
548 #define USE_PAGE_SIZE 0x1000
550 static T
AlignPageUp(T v
)
552 // todo: use actual system page size
553 uintptr_t al
= ((uintptr_t)v
) & (USE_PAGE_SIZE
- 1);
555 v
= (T
)(((uintptr_t)v
) + (USE_PAGE_SIZE
- al
));
560 ShellCopyBuffer::ShellCopyBuffer()
562 Capacity(AlignPageUp((DWORD
)COPY_BUFFER_SIZE
)),
563 Size(std::min((DWORD
)COPY_PIECE_MINIMAL
, Capacity
)),
564 // allocate page-aligned memory: IO works faster on that, also direct-io requires buffer to be aligned sometimes
565 // OSX lacks aligned_malloc so do it manually
566 Buffer(new char[Capacity
+ USE_PAGE_SIZE
]),
567 Ptr(AlignPageUp(Buffer
))
570 ShellCopyBuffer::~ShellCopyBuffer()
575 ShellCopy::ShellCopy(Panel
*SrcPanel
, // исходная панель (активная)
576 int Move
, // =1 - операция Move
577 int Link
, // =1 - Sym/Hard Link
578 int CurrentOnly
, // =1 - только текущий файл, под курсором
579 int Ask
, // =1 - выводить диалог?
581 const wchar_t *PluginDestPath
, bool ToSubdir
)
585 Flags
.ErrorMessageFlags
= MSG_WARNING
| MSG_ERRORTYPE
;
586 if (Opt
.NotifOpt
.OnFileOperation
) {
587 Flags
.ErrorMessageFlags
|= MSG_DISPLAYNOTIFY
;
590 DestList
.SetParameters(0, 0, ULF_UNIQUE
);
592 if (!(CDP
.SelCount
= SrcPanel
->GetSelCount()))
595 FARString strSelName
;
597 if (CDP
.SelCount
== 1) {
598 SrcPanel
->GetSelNameCompat(nullptr, CDP
.FileAttr
); //????
599 SrcPanel
->GetSelNameCompat(&strSelName
, CDP
.FileAttr
);
601 if (TestParentFolderName(strSelName
))
605 // Создадим объект фильтра
606 Filter
= new FileFilter(SrcPanel
, FFT_COPY
);
607 // $ 26.05.2001 OT Запретить перерисовку панелей во время копирования
608 _tran(SysLog(L
"call (*FrameManager)[0]->LockRefresh()"));
609 (*FrameManager
)[0]->Lock();
611 // Progress bar update threshold
612 CDP
.thisClass
= this;
614 CDP
.FolderPresent
= false;
615 CDP
.FilesPresent
= false;
621 Flags
.CURRENTONLY
= true;
622 ShowTotalCopySize
= Opt
.CMOpt
.CopyShowTotal
!= 0;
623 strTotalCopySizeText
.Clear();
624 SelectedFolderNameLength
= 0;
625 int DestPlugin
= ToPlugin
;
627 this->SrcPanel
= SrcPanel
;
628 DestPanel
= CtrlObject
->Cp()->GetAnotherPanel(SrcPanel
);
629 DestPanelMode
= DestPlugin
? DestPanel
->GetMode() : NORMAL_PANEL
;
630 SrcPanelMode
= SrcPanel
->GetMode();
633 ***********************************************************************
634 *** Prepare Dialog Controls
635 ***********************************************************************
637 int msh
= Move
? 1 : 0;
638 int DLG_HEIGHT
= 19 + msh
, DLG_WIDTH
= 76;
640 DialogDataEx CopyDlgData
[] = {
641 {DI_DOUBLEBOX
, 3, 1, (SHORT
)(DLG_WIDTH
- 4), (SHORT
)(DLG_HEIGHT
- 2), {}, 0, Msg::CopyDlgTitle
},
642 {DI_TEXT
, 5, 2, 0, 2, {}, 0, (Link
? Msg::CMLTargetIN
: Msg::CMLTargetTO
)},
643 {DI_EDIT
, 5, 3, 70, 3, {reinterpret_cast<DWORD_PTR
>(L
"Copy")}, DIF_FOCUS
| DIF_HISTORY
| DIF_EDITEXPAND
| DIF_USELASTHISTORY
| DIF_EDITPATH
, L
""},
644 {DI_TEXT
, 3, 4, 0, 4, {}, DIF_SEPARATOR
, L
""},
645 {DI_TEXT
, 5, 5, 0, 5, {}, 0, Msg::CopyIfFileExist
},
646 {DI_COMBOBOX
, 29, 5, 70, 5, {}, DIF_DROPDOWNLIST
| DIF_LISTNOAMPERSAND
| DIF_LISTWRAPMODE
, L
""},
647 {DI_CHECKBOX
, 5, 6, 0, 6, {}, 0, Msg::CopyMultiActions
},
648 {DI_CHECKBOX
, 5, 7, 0, 7, {}, 0, Msg::CopyAccessMode
},
649 {DI_CHECKBOX
, 5, 8, 0, 8, {}, 0, Msg::CopyXAttr
},
650 {DI_CHECKBOX
, 5, 9, 0, 9, {}, 0, Msg::CopyWriteThrough
},
651 {DI_CHECKBOX
, 5, 10, 0, 10, {}, 0, Msg::CopySparseFiles
},
652 {DI_CHECKBOX
, 5, 11, 0, 11, {}, 0, Msg::CopyUseCOW
},
653 {DI_TEXT
, 5, 12, 0, 12, {}, 0, Msg::CopySymLinkText
},
654 {DI_COMBOBOX
, 29, 12, 70, 12, {}, DIF_DROPDOWNLIST
| DIF_LISTNOAMPERSAND
| DIF_LISTWRAPMODE
, L
""},
655 {DI_TEXT
, 5, 13, 0, 13, {}, DIF_HIDDEN
, Msg::LinkCopyMoveExplainText
},
656 {DI_TEXT
, 3, (SHORT
)(13 + msh
), 0, (SHORT
)(13 + msh
), {}, DIF_SEPARATOR
, L
""},
657 {DI_CHECKBOX
, 5, (SHORT
)(14 + msh
), 0, (SHORT
)(14 + msh
), {UseFilter
? BSTATE_CHECKED
: BSTATE_UNCHECKED
}, DIF_AUTOMATION
, Msg::CopyUseFilter
},
658 {DI_TEXT
, 3, (SHORT
)(15 + msh
), 0, (SHORT
)(15 + msh
), {}, DIF_SEPARATOR
, L
""},
659 {DI_BUTTON
, 0, (SHORT
)(16 + msh
), 0, (SHORT
)(16 + msh
), {}, DIF_DEFAULT
| DIF_CENTERGROUP
, Msg::CopyDlgCopy
},
660 {DI_BUTTON
, 0, (SHORT
)(16 + msh
), 0, (SHORT
)(16 + msh
), {}, DIF_CENTERGROUP
| DIF_BTNNOCLOSE
, Msg::CopyDlgTree
},
661 {DI_BUTTON
, 0, (SHORT
)(16 + msh
), 0, (SHORT
)(16 + msh
), {}, DIF_CENTERGROUP
| DIF_BTNNOCLOSE
| DIF_AUTOMATION
| (UseFilter
? 0 : DIF_DISABLE
), Msg::CopySetFilter
},
662 {DI_BUTTON
, 0, (SHORT
)(16 + msh
), 0, (SHORT
)(16 + msh
), {}, DIF_CENTERGROUP
, Msg::CopyDlgCancel
},
663 {DI_TEXT
, 5, 2, 0, 2, {}, DIF_SHOWAMPERSAND
, L
""}
665 MakeDialogItemsEx(CopyDlgData
, CopyDlg
);
666 CopyDlg
[ID_SC_MULTITARGET
].Selected
= Opt
.CMOpt
.MultiCopy
;
667 CopyDlg
[ID_SC_WRITETHROUGH
].Selected
= Opt
.CMOpt
.WriteThrough
;
668 CopyDlg
[ID_SC_SPARSEFILES
].Selected
= Opt
.CMOpt
.SparseFiles
;
670 CopyDlg
[ID_SC_USECOW
].Selected
= Opt
.CMOpt
.UseCOW
&& (Opt
.CMOpt
.SparseFiles
== 0);
672 CopyDlg
[ID_SC_USECOW
].Flags
|= DIF_DISABLE
| DIF_HIDDEN
;
674 CopyDlg
[ID_SC_COPYACCESSMODE
].Selected
= Opt
.CMOpt
.CopyAccessMode
;
675 CopyDlg
[ID_SC_COPYXATTR
].Selected
= Opt
.CMOpt
.CopyXAttr
;
677 FarList SymLinkHowComboList
;
678 FarListItem SymLinkHowTypeItems
[3]{};
681 CopyDlg
[ID_SC_COMBOTEXT
].strData
= Msg::LinkType
;
682 // CopyDlg[ID_SC_SEPARATOR2].Flags|= DIF_DISABLE|DIF_HIDDEN;
683 CopyDlg
[ID_SC_COPYSYMLINK_TEXT
].Flags
|= DIF_DISABLE
| DIF_HIDDEN
;
684 CopyDlg
[ID_SC_COPYSYMLINK_COMBO
].Flags
|= DIF_DISABLE
| DIF_HIDDEN
;
685 CopyDlg
[ID_SC_SPARSEFILES
].Flags
|= DIF_DISABLE
| DIF_HIDDEN
;
686 CopyDlg
[ID_SC_USECOW
].Flags
|= DIF_DISABLE
| DIF_HIDDEN
;
689 SymLinkHowTypeItems
[0].Text
= Msg::LinkCopyAsIs
;
690 SymLinkHowTypeItems
[1].Text
= Msg::LinkCopySmart
;
691 SymLinkHowTypeItems
[2].Text
= Msg::LinkCopyContent
;
692 SymLinkHowComboList
.ItemsNumber
= ARRAYSIZE(SymLinkHowTypeItems
);
693 SymLinkHowComboList
.Items
= SymLinkHowTypeItems
;
694 SymLinkHowTypeItems
[std::min(Opt
.CMOpt
.HowCopySymlink
, (int)ARRAYSIZE(SymLinkHowTypeItems
) - 1)]
695 .Flags
|= LIF_SELECTED
;
697 CopyDlg
[ID_SC_COPYSYMLINK_COMBO
].ListItems
= &SymLinkHowComboList
;
699 if (Move
) // секция про перенос
701 CopyDlg
[ID_SC_MULTITARGET
].Selected
= 0;
702 CopyDlg
[ID_SC_MULTITARGET
].Flags
|= DIF_DISABLE
;
703 CopyDlg
[ID_SC_COPYSYMLINK_EXPLAIN_TEXT
].Flags
= DIF_DISABLE
;
705 // else // секция про копирование
707 // CopyDlg[ID_SC_COPYSYMLINKOUTER].Selected=1;
711 FARString strCopyStr
;
713 if (CDP
.SelCount
== 1) {
714 if (SrcPanel
->GetType() == TREE_PANEL
) {
715 FARString
strNewDir(strSelName
);
718 if (FindLastSlash(pos
, strNewDir
)) {
719 strNewDir
.Truncate(pos
);
721 if (!pos
|| strNewDir
.At(pos
- 1) == L
':')
722 strNewDir
+= WGOOD_SLASH
;
728 FARString strSelNameShort
= strSelName
;
729 strCopyStr
= (Move
? Msg::MoveFile
: (Link
? Msg::LinkFile
: Msg::CopyFile
));
730 TruncPathStr(strSelNameShort
,
732 CopyDlg
[ID_SC_TITLE
].X2
- CopyDlg
[ID_SC_TITLE
].X1
- strCopyStr
.GetLength() - 7));
733 strCopyStr
+= L
" " + strSelNameShort
;
735 // Если копируем одиночный файл, то запрещаем использовать фильтр
736 if (!(CDP
.FileAttr
& FILE_ATTRIBUTE_DIRECTORY
)) {
737 CopyDlg
[ID_SC_USEFILTER
].Selected
= 0;
738 CopyDlg
[ID_SC_USEFILTER
].Flags
|= DIF_DISABLE
;
740 } else // Объектов несколько!
742 auto NOper
= Msg::CopyFiles
;
745 NOper
= Msg::MoveFiles
;
747 NOper
= Msg::LinkFiles
;
749 // коррекция языка - про окончания
750 FormatString StrItems
;
751 StrItems
<< CDP
.SelCount
;
752 size_t LenItems
= StrItems
.strValue().GetLength();
753 auto NItems
= Msg::CMLItemsA
;
756 if ((LenItems
>= 2 && StrItems
[LenItems
- 2] == '1') || StrItems
[LenItems
- 1] >= '5'
757 || StrItems
[LenItems
- 1] == '0')
758 NItems
= Msg::CMLItemsS
;
759 else if (StrItems
[LenItems
- 1] == '1')
760 NItems
= Msg::CMLItems0
;
763 strCopyStr
.Format(NOper
, CDP
.SelCount
, NItems
.CPtr());
766 CopyDlg
[ID_SC_SOURCEFILENAME
].strData
= strCopyStr
;
767 CopyDlg
[ID_SC_TITLE
].strData
=
768 (Move
? Msg::MoveDlgTitle
: (Link
? Msg::LinkDlgTitle
: Msg::CopyDlgTitle
));
769 CopyDlg
[ID_SC_BTNCOPY
].strData
=
770 (Move
? Msg::CopyDlgRename
: (Link
? Msg::CopyDlgLink
: Msg::CopyDlgCopy
));
772 if (DestPanelMode
== PLUGIN_PANEL
) {
773 // Если противоположная панель - плагин, то дисаблим OnlyNewer //?????
776 CopyDlg[ID_SC_ACCOPY].Selected=0;
777 CopyDlg[ID_SC_ACINHERIT].Selected=0;
778 CopyDlg[ID_SC_ACLEAVE].Selected=1;
779 CopyDlg[ID_SC_ACCOPY].Flags|=DIF_DISABLE;
780 CopyDlg[ID_SC_ACINHERIT].Flags|=DIF_DISABLE;
781 CopyDlg[ID_SC_ACLEAVE].Flags|=DIF_DISABLE;
785 FARString strDestDir
;
786 DestPanel
->GetCurDir(strDestDir
);
788 AddEndSlash(strDestDir
);
790 DestPanel
->GetCurName(strSubdir
);
791 strDestDir
+= strSubdir
;
794 SrcPanel
->GetCurDir(strSrcDir
);
797 // При копировании только элемента под курсором берем его имя в кавычки, если оно содержит разделители.
798 CopyDlg
[ID_SC_TARGETEDIT
].strData
= strSelName
;
800 if (!Move
&& CopyDlg
[ID_SC_TARGETEDIT
].strData
.ContainsAnyOf(",;")) {
801 Unquote(CopyDlg
[ID_SC_TARGETEDIT
].strData
); // уберем все лишние кавычки
802 InsertQuote(CopyDlg
[ID_SC_TARGETEDIT
].strData
); // возьмем в кавычки, т.к. могут быть разделители
805 switch (DestPanelMode
) {
807 if ((strDestDir
.IsEmpty() || !DestPanel
->IsVisible() || !StrCmp(strSrcDir
, strDestDir
))
808 && CDP
.SelCount
== 1)
809 CopyDlg
[ID_SC_TARGETEDIT
].strData
= strSelName
;
811 CopyDlg
[ID_SC_TARGETEDIT
].strData
= strDestDir
;
812 AddEndSlash(CopyDlg
[ID_SC_TARGETEDIT
].strData
);
817 Если цель содержит разделители, то возьмем ее в кавычки, дабы не получить
818 ерунду при F5, Enter в панелях, когда пользователь включит MultiCopy
820 if (!Move
&& CopyDlg
[ID_SC_TARGETEDIT
].strData
.ContainsAnyOf(",;")) {
821 Unquote(CopyDlg
[ID_SC_TARGETEDIT
].strData
); // уберем все лишние кавычки
822 InsertQuote(CopyDlg
[ID_SC_TARGETEDIT
].strData
); // возьмем в кавычки, т.к. могут быть разделители
830 DestPanel
->GetOpenPluginInfo(&Info
);
831 FARString strFormat
= Info
.Format
;
832 CopyDlg
[ID_SC_TARGETEDIT
].strData
= strFormat
+ L
":";
834 while (CopyDlg
[ID_SC_TARGETEDIT
].strData
.GetLength() < 2)
835 CopyDlg
[ID_SC_TARGETEDIT
].strData
+= L
":";
837 CDP
.strPluginFormat
= CopyDlg
[ID_SC_TARGETEDIT
].strData
;
838 CDP
.strPluginFormat
.Upper();
844 FARString strInitDestDir
= CopyDlg
[ID_SC_TARGETEDIT
].strData
;
847 SrcPanel
->GetSelNameCompat(nullptr, CDP
.FileAttr
);
849 bool AddSlash
= false;
851 while (SrcPanel
->GetSelNameCompat(&strSelName
, CDP
.FileAttr
, &fd
)) {
853 if (!Filter
->FileInFilter(fd
))
857 if (CDP
.FileAttr
& FILE_ATTRIBUTE_DIRECTORY
) {
858 CDP
.FolderPresent
= true;
862 CDP
.FilesPresent
= true;
866 if (Link
) // рулесы по поводу линков (предварительные!)
868 for (int i
= ID_SC_SEPARATOR3
; i
<= ID_SC_BTNCANCEL
; i
++) {
872 CopyDlg
[ID_SC_TITLE
].Y2
-= 3;
876 // корректирем позицию " to"
877 CopyDlg
[ID_SC_TARGETTITLE
].X1
= CopyDlg
[ID_SC_TARGETTITLE
].X2
=
878 CopyDlg
[ID_SC_SOURCEFILENAME
].X1
+ (int)CopyDlg
[ID_SC_SOURCEFILENAME
].strData
.GetLength();
882 Обработка копирования мышкой - в этом случае диалог не показывается,
883 но переменные все равно инициализируются. Если произойдет неудачная
884 компиляция списка целей, то покажем диалог.
886 FARString strCopyDlgValue
;
888 strCopyDlgValue
= CopyDlg
[ID_SC_TARGETEDIT
].strData
;
889 Unquote(strCopyDlgValue
);
890 InsertQuote(strCopyDlgValue
);
892 if (!DestList
.Set(strCopyDlgValue
))
897 ***********************************************************************
898 *** Вывод и обработка диалога
899 ***********************************************************************
903 FarListItem LinkTypeItems
[2] = {}, CopyModeItems
[8] = {};
906 ComboList
.ItemsNumber
= ARRAYSIZE(LinkTypeItems
);
907 ComboList
.Items
= LinkTypeItems
;
908 ComboList
.Items
[0].Text
= Msg::LinkTypeHardlink
;
909 ComboList
.Items
[1].Text
= Msg::LinkTypeSymlink
;
911 //ComboList.Items[CDP.FilesPresent ? 0 : 1].Flags|= LIF_SELECTED;
912 ComboList
.Items
[(Opt
.MakeLinkSuggestSymlinkAlways
|| CDP
.FolderPresent
) ? 1 : 0].Flags
|= LIF_SELECTED
;
914 ComboList
.ItemsNumber
= ARRAYSIZE(CopyModeItems
);
915 ComboList
.Items
= CopyModeItems
;
916 ComboList
.Items
[CM_ASK
].Text
= Msg::CopyAsk
;
917 ComboList
.Items
[CM_OVERWRITE
].Text
= Msg::CopyOverwrite
;
918 ComboList
.Items
[CM_SKIP
].Text
= Msg::CopySkipOvr
;
919 ComboList
.Items
[CM_RENAME
].Text
= Msg::CopyRename
;
920 ComboList
.Items
[CM_APPEND
].Text
= Msg::CopyAppend
;
921 ComboList
.Items
[CM_ONLYNEWER
].Text
= Msg::CopyOnlyNewerFiles
;
922 ComboList
.Items
[CM_ASKRO
].Text
= Msg::CopyAskRO
;
923 // if uncehcked in Options->Confirmations then disable variants & set only Overwrite
924 if ( (Move
&& !Opt
.Confirm
.Move
) || (!Move
&& !Opt
.Confirm
.Copy
) ) {
925 ComboList
.Items
[CM_OVERWRITE
].Flags
= LIF_SELECTED
;
926 CopyDlg
[ID_SC_COMBO
].Flags
|= DIF_DISABLE
;
927 CopyDlg
[ID_SC_COMBOTEXT
].Flags
|= DIF_DISABLE
;
930 ComboList
.Items
[CM_ASK
].Flags
= LIF_SELECTED
;
931 ComboList
.Items
[CM_SEPARATOR
].Flags
= LIF_SEPARATOR
;
933 if (Opt
.Confirm
.RO
) {
934 ComboList
.Items
[CM_ASKRO
].Flags
= LIF_CHECKED
;
938 CopyDlg
[ID_SC_COMBO
].ListItems
= &ComboList
;
940 Dialog
Dlg(CopyDlg
, ARRAYSIZE(CopyDlg
), CopyDlgProc
, (LONG_PTR
)&CDP
);
941 Dlg
.SetHelp(Link
? L
"HardSymLink" : L
"CopyFiles");
942 Dlg
.SetId(Link
? HardSymLinkId
: (Move
? MoveFilesId
: CopyFilesId
));
943 Dlg
.SetPosition(-1, -1, DLG_WIDTH
, DLG_HEIGHT
);
944 Dlg
.SetAutomation(ID_SC_USEFILTER
, ID_SC_BTNFILTER
, DIF_DISABLE
, DIF_NONE
, DIF_NONE
, DIF_DISABLE
);
946 // $ 02.06.2001 IS + Проверим список целей и поднимем тревогу, если он содержит ошибки
952 DlgExitCode
= Dlg
.GetExitCode();
953 // Рефреш текущему времени для фильтра сразу после выхода из диалога
954 Filter
->UpdateCurrentTime();
956 if (DlgExitCode
== ID_SC_BTNCOPY
) {
959 Запомним строчку из диалога и начинаем ее мучить в зависимости от
960 состояния опции мультикопирования
962 strCopyDlgValue
= CopyDlg
[ID_SC_TARGETEDIT
].strData
;
964 Opt
.CMOpt
.MultiCopy
= CopyDlg
[ID_SC_MULTITARGET
].Selected
;
966 Opt
.CMOpt
.WriteThrough
= CopyDlg
[ID_SC_WRITETHROUGH
].Selected
;
967 Opt
.CMOpt
.CopyAccessMode
= CopyDlg
[ID_SC_COPYACCESSMODE
].Selected
;
968 Opt
.CMOpt
.CopyXAttr
= CopyDlg
[ID_SC_COPYXATTR
].Selected
;
969 Opt
.CMOpt
.SparseFiles
= CopyDlg
[ID_SC_SPARSEFILES
].Selected
;
970 Opt
.CMOpt
.UseCOW
= CopyDlg
[ID_SC_USECOW
].Selected
;
972 if (!CopyDlg
[ID_SC_MULTITARGET
].Selected
|| !strCopyDlgValue
.ContainsAnyOf(",;")) // отключено multi*
974 // уберем лишние кавычки
975 Unquote(strCopyDlgValue
);
976 // добавим кавычки, чтобы "список" удачно скомпилировался вне
977 // зависимости от наличия разделителей в оном
978 InsertQuote(strCopyDlgValue
);
981 if (DestList
.Set(strCopyDlgValue
)) {
982 // Запомнить признак использования фильтра. KM
983 UseFilter
= CopyDlg
[ID_SC_USEFILTER
].Selected
;
986 Message(MSG_WARNING
, 1, Msg::Warning
, Msg::CopyIncorrectTargetList
, Msg::Ok
);
992 if (DlgExitCode
== ID_SC_BTNCANCEL
|| DlgExitCode
< 0
993 || (CopyDlg
[ID_SC_BTNCOPY
].Flags
& DIF_DISABLE
)) {
1002 ***********************************************************************
1003 *** Стадия подготовки данных после диалога
1004 ***********************************************************************
1006 Flags
.WRITETHROUGH
= Flags
.COPYACCESSMODE
= Flags
.COPYXATTR
= Flags
.SPARSEFILES
= Flags
.USECOW
= false;
1007 Flags
.SYMLINK
= COPY_SYMLINK_ASIS
;
1008 ReadOnlyDelMode
= ReadOnlyOvrMode
= OvrMode
= SkipMode
= SkipDeleteMode
= -1;
1011 switch (CopyDlg
[ID_SC_COMBO
].ListPos
) {
1022 RPT
= RP_SYMLINKFILE
;
1025 RPT
= RP_SYMLINKDIR
;
1029 ReadOnlyOvrMode
= CDP
.AskRO
? -1 : 1;
1031 switch (CopyDlg
[ID_SC_COMBO
].ListPos
) {
1040 ReadOnlyOvrMode
= CDP
.AskRO
? -1 : 3;
1049 Flags
.ONLYNEWERFILES
= true;
1053 Opt
.CMOpt
.HowCopySymlink
= CopyDlg
[ID_SC_COPYSYMLINK_COMBO
].ListPos
;
1054 switch (Opt
.CMOpt
.HowCopySymlink
) {
1056 Flags
.SYMLINK
= COPY_SYMLINK_SMART
;
1059 Flags
.SYMLINK
= COPY_SYMLINK_ASFILE
;
1064 if (DestPlugin
&& !StrCmp(CopyDlg
[ID_SC_TARGETEDIT
].strData
, strInitDestDir
)) {
1069 if (Opt
.CMOpt
.WriteThrough
)
1070 Flags
.WRITETHROUGH
= true;
1071 if (Opt
.CMOpt
.CopyAccessMode
)
1072 Flags
.COPYACCESSMODE
= true;
1073 if (Opt
.CMOpt
.CopyXAttr
)
1074 Flags
.COPYXATTR
= true;
1075 if (Opt
.CMOpt
.SparseFiles
)
1076 Flags
.SPARSEFILES
= true;
1077 if (Opt
.CMOpt
.UseCOW
&& Opt
.CMOpt
.SparseFiles
== 0)
1078 Flags
.USECOW
= true;
1080 if (CDP
.SelCount
== 1)
1081 AddSlash
= false; //???
1083 if (DestPlugin
== 2) {
1085 strCopyDlgValue
= PluginDestPath
;
1090 if ((Opt
.Diz
.UpdateMode
== DIZ_UPDATE_IF_DISPLAYED
&& SrcPanel
->IsDizDisplayed())
1091 || Opt
.Diz
.UpdateMode
== DIZ_UPDATE_ALWAYS
) {
1092 CtrlObject
->Cp()->LeftPanel
->ReadDiz();
1093 CtrlObject
->Cp()->RightPanel
->ReadDiz();
1096 DestPanel
->CloseFile();
1097 strDestDizPath
.Clear();
1098 SrcPanel
->SaveSelection();
1099 // нужно ли показывать время копирования?
1100 bool ShowCopyTime
= (Opt
.CMOpt
.CopyTimeRule
& COPY_RULE_FILES
) != 0;
1102 ***********************************************************************
1103 **** Здесь все подготовительные операции закончены, можно приступать
1104 **** к процессу Copy/Move/Link
1105 ***********************************************************************
1107 int NeedDizUpdate
= FALSE
;
1108 int NeedUpdateAPanel
= FALSE
;
1109 Flags
.UPDATEPPANEL
= true;
1111 ЕСЛИ ПРИНЯТЬ В КАЧЕСТВЕ РАЗДЕЛИТЕЛЯ ПУТЕЙ, НАПРИМЕР ';',
1112 то нужно парсить CopyDlgValue на предмет MultiCopy и
1113 вызывать CopyFileTree нужное количество раз.
1118 if (DestList
.Set(strCopyDlgValue
)) // если список успешно "скомпилировался"
1120 const wchar_t *NamePtr
;
1121 FARString strNameTmp
;
1122 // посчитаем количество целей.
1123 CountTarget
= DestList
.GetTotal();
1125 TotalCopySize
= TotalCopiedSize
= TotalSkippedSize
= 0;
1126 ProgressUpdateTime
= 0;
1128 // Запомним время начала
1130 CopyStartTime
= GetProcessUptimeMSec();
1131 WaitUserTime
= OldCalcTime
= 0;
1134 if (CountTarget
> 1)
1137 for (size_t DLI
= 0; nullptr != (NamePtr
= DestList
.Get(DLI
)); ++DLI
) {
1139 strNameTmp
= NamePtr
;
1141 if (!StrCmp(strNameTmp
, L
"..") && IsLocalRootPath(strSrcDir
)) {
1142 if (!Message(MSG_WARNING
, 2, Msg::Error
,
1143 ((!Move
? Msg::CannotCopyToTwoDot
: Msg::CannotMoveToTwoDot
)),
1144 Msg::CannotCopyMoveToTwoDot
, Msg::CopySkip
, Msg::CopyCancel
))
1150 if (DestList
.IsLastElement(DLI
)) { // для последней операции нужно учесть моменты связанные с операцией Move.
1151 Flags
.COPYLASTTIME
= true;
1156 // Если выделенных элементов больше 1 и среди них есть каталог, то всегда
1157 // делаем так, чтобы на конце был '/'
1158 // делаем так не всегда, а только когда NameTmp не является маской.
1159 if (AddSlash
&& !strNameTmp
.ContainsAnyOf("*?"))
1160 AddEndSlash(strNameTmp
);
1162 if (CDP
.SelCount
== 1 && !CDP
.FolderPresent
) {
1163 ShowTotalCopySize
= false;
1164 TotalFilesToProcess
= 1;
1168 if (CDP
.SelCount
== 1 && CDP
.FolderPresent
1169 && CheckUpdateAnotherPanel(SrcPanel
, strSelName
)) {
1170 NeedUpdateAPanel
= TRUE
;
1174 CP
= new CopyProgress(Move
!= 0, ShowTotalCopySize
, ShowCopyTime
);
1175 // Обнулим инфу про дизы
1176 strDestDizPath
.Clear();
1177 Flags
.DIZREAD
= false;
1178 // сохраним выделение
1179 SrcPanel
->SaveSelection();
1180 const auto OldFlagsSYMLINK
= Flags
.SYMLINK
;
1181 // собственно - один проход копирования
1182 // Mantis#45: Необходимо привести копирование ссылок на папки с NTFS на FAT к более логичному виду
1184 // todo: If dst does not support symlinks
1185 // Flags.SYMLINK = COPY_SYMLINK_ASFILE;
1187 PreRedraw
.Push(PR_ShellCopyMsg
);
1188 PreRedrawItem preRedrawItem
= PreRedraw
.Peek();
1189 preRedrawItem
.Param
.Param1
= CP
;
1190 PreRedraw
.SetParam(preRedrawItem
.Param
);
1191 int I
= CopyFileTree(strNameTmp
);
1193 Flags
.SYMLINK
= OldFlagsSYMLINK
;
1195 if (I
== COPY_CANCEL
) {
1196 NeedDizUpdate
= TRUE
;
1200 // если "есть порох в пороховницах" - восстановим выделение
1201 if (!DestList
.IsLastElement(DLI
))
1202 SrcPanel
->RestoreSelection();
1204 // Позаботимся о дизах.
1205 if (!strDestDizPath
.IsEmpty()) {
1206 FARString strDestDizName
;
1207 DestDiz
.GetDizName(strDestDizName
);
1208 DWORD Attr
= apiGetFileAttributes(strDestDizName
);
1209 int DestReadOnly
= (Attr
!= INVALID_FILE_ATTRIBUTES
&& (Attr
& FILE_ATTRIBUTE_READONLY
));
1211 if (DestList
.IsLastElement(DLI
)) // Скидываем только во время последней Op.
1212 if (Move
&& !DestReadOnly
)
1213 SrcPanel
->FlushDiz();
1215 DestDiz
.Flush(strDestDizPath
);
1219 _LOGCOPYR(else SysLog(L
"Error: DestList.Set(CopyDlgValue) return FALSE"));
1222 ***********************************************************************
1223 *** заключительеая стадия процесса
1224 *** восстанавливаем/дизим/редравим
1225 ***********************************************************************
1228 if (NeedDizUpdate
) // при мультикопировании может быть обрыв, но нам все
1229 { // равно нужно апдейтить дизы!
1230 if (!strDestDizPath
.IsEmpty()) {
1231 FARString strDestDizName
;
1232 DestDiz
.GetDizName(strDestDizName
);
1233 DWORD Attr
= apiGetFileAttributes(strDestDizName
);
1234 int DestReadOnly
= (Attr
!= INVALID_FILE_ATTRIBUTES
&& (Attr
& FILE_ATTRIBUTE_READONLY
));
1236 if (Move
&& !DestReadOnly
)
1237 SrcPanel
->FlushDiz();
1239 DestDiz
.Flush(strDestDizPath
);
1243 SrcPanel
->Update(UPDATE_KEEP_SELECTION
);
1245 if (CDP
.SelCount
== 1 && !strRenamedName
.IsEmpty())
1246 SrcPanel
->GoToFile(strRenamedName
);
1248 if (NeedUpdateAPanel
&& CDP
.FileAttr
!= INVALID_FILE_ATTRIBUTES
1249 && (CDP
.FileAttr
& FILE_ATTRIBUTE_DIRECTORY
) && DestPanelMode
!= PLUGIN_PANEL
) {
1250 FARString strTmpSrcDir
;
1251 SrcPanel
->GetCurDir(strTmpSrcDir
);
1252 DestPanel
->SetCurDir(strTmpSrcDir
, FALSE
);
1255 // проверим "нужность" апдейта пассивной панели
1256 if (Flags
.UPDATEPPANEL
) {
1257 DestPanel
->SortFileList(TRUE
);
1258 DestPanel
->Update(UPDATE_KEEP_SELECTION
| UPDATE_SECONDARY
);
1261 if (SrcPanelMode
== PLUGIN_PANEL
)
1262 SrcPanel
->SetPluginModified();
1264 CtrlObject
->Cp()->Redraw();
1266 if (Opt
.NotifOpt
.OnFileOperation
) {
1267 DisplayNotification(Msg::FileOperationComplete
, strSelName
); // looks like strSelName is best choice
1271 LONG_PTR WINAPI
CopyDlgProc(HANDLE hDlg
, int Msg
, int Param1
, LONG_PTR Param2
)
1273 #define DM_CALLTREE (DM_USER + 1)
1274 #define DM_SWITCHRO (DM_USER + 2)
1275 CopyDlgParam
*DlgParam
= (CopyDlgParam
*)SendDlgMessage(hDlg
, DM_GETDLGDATA
, 0, 0);
1279 SendDlgMessage(hDlg
, DM_SETCOMBOBOXEVENT
, ID_SC_COMBO
, CBET_KEY
| CBET_MOUSE
);
1280 SendDlgMessage(hDlg
, DM_SETMOUSEEVENTNOTIFY
, TRUE
, 0);
1283 FarListGetItem LGI
= {CM_ASKRO
};
1284 SendDlgMessage(hDlg
, DM_LISTGETITEM
, ID_SC_COMBO
, (LONG_PTR
)&LGI
);
1286 if (LGI
.Item
.Flags
& LIF_CHECKED
)
1287 LGI
.Item
.Flags
&= ~LIF_CHECKED
;
1289 LGI
.Item
.Flags
|= LIF_CHECKED
;
1291 SendDlgMessage(hDlg
, DM_LISTUPDATE
, ID_SC_COMBO
, (LONG_PTR
)&LGI
);
1292 SendDlgMessage(hDlg
, DM_REDRAW
, 0, 0);
1297 if (Param1
== ID_SC_USEFILTER
) // "Use filter"
1299 UseFilter
= (int)Param2
;
1303 if (Param1
== ID_SC_BTNTREE
) // Tree
1305 SendDlgMessage(hDlg
, DM_CALLTREE
, 0, 0);
1307 } else if (Param1
== ID_SC_BTNCOPY
) {
1308 SendDlgMessage(hDlg
, DM_CLOSE
, ID_SC_BTNCOPY
, 0);
1309 } else if (Param1
== ID_SC_SPARSEFILES
) {
1310 SendDlgMessage(hDlg
, DM_SETCHECK
, ID_SC_USECOW
, BSTATE_UNCHECKED
);
1311 } else if (Param1
== ID_SC_USECOW
) {
1312 SendDlgMessage(hDlg
, DM_SETCHECK
, ID_SC_SPARSEFILES
, BSTATE_UNCHECKED
);
1315 else if(Param1 == ID_SC_ONLYNEWER && (DlgParam->thisClass->Flags.LINK))
1317 // подсократим код путем эмуляции телодвижений в строке ввода :-))
1318 SendDlgMessage(hDlg,DN_EDITCHANGE,ID_SC_TARGETEDIT,0);
1321 else if (Param1
== ID_SC_BTNFILTER
) // Filter
1323 Filter
->FilterEdit();
1329 case DM_KEY
: // по поводу дерева!
1331 if (Param2
== KEY_ALTF10
|| Param2
== KEY_F10
|| Param2
== KEY_SHIFTF10
) {
1332 DlgParam
->AltF10
= Param2
== KEY_ALTF10
? 1 : (Param2
== KEY_SHIFTF10
? 2 : 0);
1333 SendDlgMessage(hDlg
, DM_CALLTREE
, DlgParam
->AltF10
, 0);
1337 if (Param1
== ID_SC_COMBO
) {
1338 if (Param2
== KEY_ENTER
|| Param2
== KEY_NUMENTER
|| Param2
== KEY_INS
1339 || Param2
== KEY_NUMPAD0
|| Param2
== KEY_SPACE
) {
1340 if (SendDlgMessage(hDlg
, DM_LISTGETCURPOS
, ID_SC_COMBO
, 0) == CM_ASKRO
)
1341 return SendDlgMessage(hDlg
, DM_SWITCHRO
, 0, 0);
1347 if (Param1
== ID_SC_COMBO
) {
1348 if (SendDlgMessage(hDlg
, DM_LISTGETCURPOS
, ID_SC_COMBO
, 0) == CM_ASKRO
) {
1349 SendDlgMessage(hDlg
, DM_SWITCHRO
, 0, 0);
1356 if (SendDlgMessage(hDlg
, DM_GETDROPDOWNOPENED
, ID_SC_COMBO
, 0)) {
1357 MOUSE_EVENT_RECORD
*mer
= (MOUSE_EVENT_RECORD
*)Param2
;
1359 if (SendDlgMessage(hDlg
, DM_LISTGETCURPOS
, ID_SC_COMBO
, 0) == CM_ASKRO
&& mer
->dwButtonState
1360 && !(mer
->dwEventFlags
& MOUSE_MOVED
)) {
1361 SendDlgMessage(hDlg
, DM_SWITCHRO
, 0, 0);
1371 + При мультикопировании добавляем выбранный в "дереве" каталог к уже
1372 существующему списку через точку с запятой.
1373 - Баг: при мультикопировании выбранный в "дереве" каталог не
1374 заключался в кавычки, если он содержал в своем
1375 имени символы-разделители.
1376 - Баг: неправильно работало Shift-F10, если строка ввода содержала
1378 - Баг: неправильно работало Shift-F10 при мультикопировании -
1379 показывался корневой каталог, теперь показывается самый первый каталог
1382 BOOL MultiCopy
= SendDlgMessage(hDlg
, DM_GETCHECK
, ID_SC_MULTITARGET
, 0) == BSTATE_CHECKED
;
1383 FARString strOldFolder
;
1385 FarDialogItemData Data
;
1386 nLength
= (int)SendDlgMessage(hDlg
, DM_GETTEXTLENGTH
, ID_SC_TARGETEDIT
, 0);
1387 Data
.PtrData
= strOldFolder
.GetBuffer(nLength
+ 1);
1388 Data
.PtrLength
= nLength
;
1389 SendDlgMessage(hDlg
, DM_GETTEXT
, ID_SC_TARGETEDIT
, (LONG_PTR
)&Data
);
1390 strOldFolder
.ReleaseBuffer();
1391 FARString strNewFolder
;
1393 if (DlgParam
->AltF10
== 2) {
1394 strNewFolder
= strOldFolder
;
1397 UserDefinedList
DestList(0, 0, ULF_UNIQUE
);
1399 if (DestList
.Set(strOldFolder
)) {
1400 const wchar_t *NamePtr
= DestList
.Get(0);
1403 strNewFolder
= NamePtr
;
1407 if (strNewFolder
.IsEmpty())
1408 DlgParam
->AltF10
= -1;
1409 else // убираем лишний слеш
1410 DeleteEndSlash(strNewFolder
);
1413 if (DlgParam
->AltF10
!= -1) {
1415 FARString strNewFolder2
;
1416 FolderTree::Present(strNewFolder2
,
1417 (DlgParam
->AltF10
== 1
1419 : (DlgParam
->AltF10
== 2 ? MODALTREE_FREE
: MODALTREE_ACTIVE
)),
1421 strNewFolder
= strNewFolder2
;
1424 if (!strNewFolder
.IsEmpty()) {
1425 AddEndSlash(strNewFolder
);
1427 if (MultiCopy
) // мультикопирование
1429 // Добавим кавычки, если имя каталога содержит символы-разделители
1430 if (strNewFolder
.ContainsAnyOf(";,"))
1431 InsertQuote(strNewFolder
);
1433 if (strOldFolder
.GetLength())
1434 strOldFolder
+= L
";"; // добавим разделитель к непустому списку
1436 strOldFolder
+= strNewFolder
;
1437 strNewFolder
= strOldFolder
;
1440 SendDlgMessage(hDlg
, DM_SETTEXTPTR
, ID_SC_TARGETEDIT
, (LONG_PTR
)strNewFolder
.CPtr());
1441 SendDlgMessage(hDlg
, DM_SETFOCUS
, ID_SC_TARGETEDIT
, 0);
1445 DlgParam
->AltF10
= 0;
1449 if (Param1
== ID_SC_BTNCOPY
) {
1450 FarListGetItem LGI
= {CM_ASKRO
};
1451 SendDlgMessage(hDlg
, DM_LISTGETITEM
, ID_SC_COMBO
, (LONG_PTR
)&LGI
);
1453 if (LGI
.Item
.Flags
& LIF_CHECKED
)
1454 DlgParam
->AskRO
= TRUE
;
1459 return DefDlgProc(hDlg
, Msg
, Param1
, Param2
);
1462 ShellCopy::~ShellCopy()
1464 _tran(SysLog(L
"[%p] ShellCopy::~ShellCopy(), CopyBuffer=%p", this, CopyBuffer
));
1466 // $ 26.05.2001 OT Разрешить перерисовку панелей
1467 _tran(SysLog(L
"call (*FrameManager)[0]->UnlockRefresh()"));
1468 (*FrameManager
)[0]->Unlock();
1469 (*FrameManager
)[0]->Refresh();
1471 if (Filter
) // Уничтожим объект фильтра
1480 COPY_CODES
ShellCopy::CopyFileTree(const wchar_t *Dest
)
1482 ChangePriority
ChPriority(ChangePriority::NORMAL
);
1483 // SaveScreen SaveScr;
1484 DWORD DestAttr
= INVALID_FILE_ATTRIBUTES
;
1485 FARString strSelName
;
1489 if (!(Length
= StrLength(Dest
)) || !StrCmp(Dest
, L
"."))
1490 return COPY_FAILURE
; //????
1492 SetCursorType(FALSE
, 0);
1494 if (!TotalCopySize
) {
1495 strTotalCopySizeText
.Clear();
1497 // ! Не сканируем каталоги при создании линков
1498 if (ShowTotalCopySize
&& !Flags
.LINK
&& !CalcTotalSize())
1499 return COPY_FAILURE
;
1504 // Создание структуры каталогов в месте назначения
1505 FARString strNewPath
= Dest
;
1507 if (!IsSlash(strNewPath
.At(strNewPath
.GetLength() - 1)) && SrcPanel
->GetSelCount() > 1
1508 && !strNewPath
.ContainsAnyOf("*?")
1509 && apiGetFileAttributes(strNewPath
) == INVALID_FILE_ATTRIBUTES
) {
1510 switch (Message(FMSG_WARNING
, 3, Msg::Warning
, strNewPath
, Msg::CopyDirectoryOrFile
,
1511 Msg::CopyDirectoryOrFileDirectory
, Msg::CopyDirectoryOrFileFile
, Msg::Cancel
)) {
1513 AddEndSlash(strNewPath
);
1524 if (FindLastSlash(pos
, strNewPath
)) {
1525 strNewPath
.Truncate(pos
+ 1);
1527 DWORD Attr
= apiGetFileAttributes(strNewPath
);
1529 if (Attr
== INVALID_FILE_ATTRIBUTES
) {
1530 if (apiCreateDirectory(strNewPath
, nullptr))
1531 TreeList::AddTreeName(strNewPath
);
1533 CreatePath(strNewPath
);
1534 } else if (!(Attr
& FILE_ATTRIBUTE_DIRECTORY
)) {
1535 Message(MSG_WARNING
, 1, Msg::Error
, Msg::CopyCannotCreateFolder
, strNewPath
, Msg::Ok
);
1536 return COPY_FAILURE
;
1540 DestAttr
= apiGetFileAttributes(Dest
);
1542 // Выставим признак "Тот же диск"
1543 bool AllowMoveByOS
= false;
1546 FARString strTmpSrcDir
;
1547 SrcPanel
->GetCurDir(strTmpSrcDir
);
1548 AllowMoveByOS
= (CheckDisksProps(strTmpSrcDir
, Dest
, CHECKEDPROPS_ISSAMEDISK
)) != 0;
1551 // Основной цикл копирования одной порции.
1552 SrcPanel
->GetSelNameCompat(nullptr, FileAttr
);
1553 while (SrcPanel
->GetSelNameCompat(&strSelName
, FileAttr
)) {
1554 SelectedPanelItems
.emplace_back();
1555 ConvertNameToFull(strSelName
, SelectedPanelItems
.back());
1558 SrcPanel
->GetSelNameCompat(nullptr, FileAttr
);
1560 while (SrcPanel
->GetSelNameCompat(&strSelName
, FileAttr
)) {
1561 FARString strDest
= Dest
;
1563 if (FileAttr
& FILE_ATTRIBUTE_DIRECTORY
)
1564 SelectedFolderNameLength
= (int)strSelName
.GetLength();
1566 SelectedFolderNameLength
= 0;
1568 if (strDest
.ContainsAnyOf("*?"))
1569 ConvertWildcards(strSelName
, strDest
, SelectedFolderNameLength
);
1571 DestAttr
= apiGetFileAttributes(strDest
);
1573 FARString strDestPath
= strDest
;
1574 FAR_FIND_DATA_EX SrcData
;
1576 int CopyCode
= COPY_SUCCESS
, KeepPathPos
;
1577 Flags
.OVERWRITENEXT
= false;
1579 KeepPathPos
= (int)(PointToName(strSelName
) - strSelName
.CPtr());
1581 if (RPT
== RP_JUNCTION
|| RPT
== RP_SYMLINK
|| RPT
== RP_SYMLINKFILE
|| RPT
== RP_SYMLINKDIR
) {
1582 switch (MkSymLink(strSelName
, strDest
, RPT
, true)) {
1587 // Отметим (Ins) несколько каталогов, ALT-F6 Enter - выделение с папок не снялось.
1588 if (!Flags
.CURRENTONLY
&& Flags
.COPYLASTTIME
)
1589 SrcPanel
->ClearLastGetSelection();
1593 return COPY_FAILURE
;
1596 // проверка на вшивость ;-)
1598 if (!apiGetFindDataForExactPathName(strSelName
, SrcData
)) {
1599 strDestPath
= strSelName
;
1600 CP
->SetNames(strSelName
, strDestPath
);
1602 if (Message(MSG_WARNING
, 2, Msg::Error
, Msg::CopyCannotFind
, strSelName
, Msg::Skip
,
1605 return COPY_FAILURE
;
1610 /// fprintf(stderr, "!!!!! RPT=%x for '%ls'\n", (SrcData.dwFileAttributes&FILE_ATTRIBUTE_REPARSE_POINT), strSelName.CPtr());
1613 // KeepPathPos=PointToName(SelName)-SelName;
1617 // Тыкс, а как на счет "тот же диск"?
1618 if (KeepPathPos
&& PointToName(strDest
) == strDest
) {
1619 strDestPath
= strSelName
;
1620 strDestPath
.Truncate(KeepPathPos
);
1621 strDestPath
+= strDest
;
1622 AllowMoveByOS
= true;
1625 if (UseFilter
|| !AllowMoveByOS
|| // can't move across different devices
1626 // if any symlinks copy may occur - parse whole tree
1627 ((SrcData
.dwFileAttributes
1628 & (FILE_ATTRIBUTE_REPARSE_POINT
| FILE_ATTRIBUTE_DIRECTORY
))
1630 && Flags
.SYMLINK
!= COPY_SYMLINK_ASIS
)) {
1631 CopyCode
= COPY_FAILURE
;
1633 CopyCode
= ShellCopyOneFile(strSelName
, SrcData
, strDestPath
, KeepPathPos
, 1);
1635 if (CopyCode
== COPY_SUCCESS_MOVE
) {
1636 if (!strDestDizPath
.IsEmpty()) {
1637 if (!strRenamedName
.IsEmpty()) {
1638 DestDiz
.DeleteDiz(strSelName
);
1639 SrcPanel
->CopyDiz(strSelName
, strRenamedName
, &DestDiz
);
1641 if (strCopiedName
.IsEmpty())
1642 strCopiedName
= strSelName
;
1644 SrcPanel
->CopyDiz(strSelName
, strCopiedName
, &DestDiz
);
1645 SrcPanel
->DeleteDiz(strSelName
);
1652 if (CopyCode
== COPY_CANCEL
)
1655 if (CopyCode
== COPY_NEXT
) {
1656 uint64_t CurSize
= SrcData
.nFileSize
;
1657 TotalCopiedSize
= TotalCopiedSize
- CurCopiedSize
+ CurSize
;
1658 TotalSkippedSize
= TotalSkippedSize
+ CurSize
- CurCopiedSize
;
1662 if (!Flags
.MOVE
|| CopyCode
== COPY_FAILURE
)
1663 Flags
.OVERWRITENEXT
= true;
1667 if (!Flags
.MOVE
|| CopyCode
== COPY_FAILURE
) {
1668 FARString strCopyDest
= strDest
;
1670 CopyCode
= ShellCopyOneFile(strSelName
, SrcData
, strCopyDest
, KeepPathPos
, 0);
1672 Flags
.OVERWRITENEXT
= false;
1674 if (CopyCode
== COPY_CANCEL
)
1677 if (CopyCode
!= COPY_SUCCESS
) {
1678 uint64_t CurSize
= SrcData
.nFileSize
;
1680 if (CopyCode
!= COPY_NOFILTER
) //????
1681 TotalCopiedSize
= TotalCopiedSize
- CurCopiedSize
+ CurSize
;
1683 if (CopyCode
== COPY_NEXT
)
1684 TotalSkippedSize
= TotalSkippedSize
+ CurSize
- CurCopiedSize
;
1690 if (CopyCode
== COPY_SUCCESS
&& !strDestDizPath
.IsEmpty()) {
1691 if (strCopiedName
.IsEmpty())
1692 strCopiedName
= strSelName
;
1694 SrcPanel
->CopyDiz(strSelName
, strCopiedName
, &DestDiz
);
1698 Mantis#44 - Потеря данных при копировании ссылок на папки
1699 если каталог (или нужно копировать симлинк) - придется рекурсивно спускаться...
1701 if (RPT
!= RP_SYMLINKFILE
&& (SrcData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) != 0
1702 && ((SrcData
.dwFileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
) == 0
1703 || ((SrcData
.dwFileAttributes
1704 & (FILE_ATTRIBUTE_REPARSE_POINT
| FILE_ATTRIBUTE_BROKEN
))
1705 == FILE_ATTRIBUTE_REPARSE_POINT
1706 && Flags
.SYMLINK
== COPY_SYMLINK_ASFILE
))) {
1708 FARString strSubName
;
1709 FARString strFullName
;
1710 ScanTree
ScTree(TRUE
, TRUE
, Flags
.SYMLINK
== COPY_SYMLINK_ASFILE
);
1711 strSubName
= strSelName
;
1712 strSubName
+= WGOOD_SLASH
;
1714 if (DestAttr
== INVALID_FILE_ATTRIBUTES
)
1715 KeepPathPos
= (int)strSubName
.GetLength();
1717 int NeedRename
= !((SrcData
.dwFileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
)
1718 && Flags
.SYMLINK
== COPY_SYMLINK_ASFILE
&& Flags
.MOVE
);
1719 ScTree
.SetFindPath(strSubName
, L
"*", FSCANTREE_FILESFIRST
);
1720 while (ScTree
.GetNextName(&SrcData
, strFullName
)) {
1721 if (UseFilter
&& (SrcData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)) {
1723 Просто пропустить каталог недостаточно - если каталог помечен в
1724 фильтре как некопируемый, то следует пропускать и его и всё его
1727 if (!Filter
->FileInFilter(SrcData
)) {
1733 int AttemptToMove
= FALSE
;
1735 if (Flags
.MOVE
&& !UseFilter
&& AllowMoveByOS
1736 && (SrcData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) == 0
1737 && ((SrcData
.dwFileAttributes
1738 & (FILE_ATTRIBUTE_REPARSE_POINT
| FILE_ATTRIBUTE_BROKEN
))
1740 || Flags
.SYMLINK
== COPY_SYMLINK_ASIS
)) {
1741 AttemptToMove
= TRUE
;
1742 int Ret
= COPY_SUCCESS
;
1743 FARString strCopyDest
= strDest
;
1745 Ret
= ShellCopyOneFile(strFullName
, SrcData
, strCopyDest
, KeepPathPos
,
1753 uint64_t CurSize
= SrcData
.nFileSize
;
1754 TotalCopiedSize
= TotalCopiedSize
- CurCopiedSize
+ CurSize
;
1755 TotalSkippedSize
= TotalSkippedSize
+ CurSize
- CurCopiedSize
;
1758 case COPY_SUCCESS_MOVE
: {
1763 if (!NeedRename
) // вариант при перемещении содержимого симлинка с опцией "копировать содержимое сим..."
1765 uint64_t CurSize
= SrcData
.nFileSize
;
1766 TotalCopiedSize
= TotalCopiedSize
- CurCopiedSize
+ CurSize
;
1767 TotalSkippedSize
= TotalSkippedSize
+ CurSize
- CurCopiedSize
;
1768 continue; // ... т.к. мы ЭТО не мувили, а скопировали, то все, на этом закончим бодаться с этим файлов
1773 int SaveOvrMode
= OvrMode
;
1778 FARString strCopyDest
= strDest
;
1780 SubCopyCode
= ShellCopyOneFile(strFullName
, SrcData
, strCopyDest
, KeepPathPos
, 0);
1783 OvrMode
= SaveOvrMode
;
1786 if (SubCopyCode
== COPY_CANCEL
)
1789 if (SubCopyCode
== COPY_NEXT
) {
1790 uint64_t CurSize
= SrcData
.nFileSize
;
1791 TotalCopiedSize
= TotalCopiedSize
- CurCopiedSize
+ CurSize
;
1792 TotalSkippedSize
= TotalSkippedSize
+ CurSize
- CurCopiedSize
;
1795 if (SubCopyCode
== COPY_SUCCESS
) {
1797 if (SrcData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
1798 if (ScTree
.IsDirSearchDone()
1799 || ((SrcData
.dwFileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
)
1800 && (Flags
.SYMLINK
!= COPY_SYMLINK_ASFILE
))) {
1801 TemporaryMakeWritable
tmw(strFullName
);
1802 // if (SrcData.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
1803 // apiMakeWritable(strFullName); //apiSetFileAttributes(strFullName,FILE_ATTRIBUTE_NORMAL);
1805 if (apiRemoveDirectory(strFullName
))
1806 TreeList::DelTreeName(strFullName
);
1810 здесь нужны проверка на FSCANTREE_INSIDEJUNCTION, иначе
1811 при мовинге будет удаление файла, что крайне неправильно!
1813 else if (!ScTree
.IsInsideSymlink()) {
1814 if (DeleteAfterMove(strFullName
, SrcData
.dwFileAttributes
) == COPY_CANCEL
)
1821 if (Flags
.MOVE
&& CopyCode
== COPY_SUCCESS
) {
1822 TemporaryMakeWritable
tmw(strSelName
);
1823 // if (FileAttr & FILE_ATTRIBUTE_READONLY)
1824 // apiMakeWritable(strSelName); //apiSetFileAttributes(strSelName,FILE_ATTRIBUTE_NORMAL);
1826 if (apiRemoveDirectory(strSelName
)) {
1827 TreeList::DelTreeName(strSelName
);
1829 if (!strDestDizPath
.IsEmpty())
1830 SrcPanel
->DeleteDiz(strSelName
);
1833 } else if (Flags
.MOVE
&& CopyCode
== COPY_SUCCESS
) {
1836 if ((DeleteCode
= DeleteAfterMove(strSelName
, FileAttr
)) == COPY_CANCEL
)
1839 if (DeleteCode
== COPY_SUCCESS
&& !strDestDizPath
.IsEmpty())
1840 SrcPanel
->DeleteDiz(strSelName
);
1843 if (!Flags
.CURRENTONLY
&& Flags
.COPYLASTTIME
) {
1844 SrcPanel
->ClearLastGetSelection();
1849 SetEnqueuedDirectoriesAttributes();
1851 return COPY_SUCCESS
; // COPY_SUCCESS_MOVE???
1854 void ShellCopy::EnqueueDirectoryAttributes(const FAR_FIND_DATA_EX
&SrcData
, FARString
&strDest
)
1856 DirectoriesAttributes
.emplace_back();
1857 auto &cdb
= DirectoriesAttributes
.back();
1858 cdb
.Path
= strDest
.GetMB();
1859 cdb
.ftUnixAccessTime
= SrcData
.ftUnixAccessTime
;
1860 cdb
.ftUnixModificationTime
= SrcData
.ftUnixModificationTime
;
1861 cdb
.dwUnixMode
= SrcData
.dwUnixMode
;
1864 void ShellCopy::SetEnqueuedDirectoriesAttributes()
1866 std::sort(DirectoriesAttributes
.begin(), DirectoriesAttributes
.end(),
1867 [&](const CopiedDirectory
&a
, const CopiedDirectory
&b
) -> bool {
1868 return b
.Path
< a
.Path
;
1870 for (const auto &cd
: DirectoriesAttributes
) {
1871 // fprintf(stderr, "!!! '%s'\n", cd.Path.c_str());
1872 struct timespec ts
[2] = {};
1873 WINPORT(FileTime_Win32ToUnix
)(&cd
.ftUnixAccessTime
, &ts
[0]);
1874 WINPORT(FileTime_Win32ToUnix
)(&cd
.ftUnixModificationTime
, &ts
[1]);
1875 if (sdc_utimens(cd
.Path
.c_str(), ts
) == -1) {
1876 fprintf(stderr
, "sdc_utimens error %d for '%s'\n", errno
, cd
.Path
.c_str());
1878 if (Flags
.COPYACCESSMODE
) {
1879 if (sdc_chmod(cd
.Path
.c_str(), cd
.dwUnixMode
) == -1) {
1880 fprintf(stderr
, "sdc_chmod mode=0%o error %d for '%s'\n", cd
.dwUnixMode
, errno
,
1885 DirectoriesAttributes
.clear();
1888 bool ShellCopy::IsSymlinkTargetAlsoCopied(const wchar_t *SymLink
)
1890 if (!SymLink
|| !*SymLink
)
1893 FARString strTarget
;
1894 ConvertNameToReal(SymLink
, strTarget
);
1895 for (const auto &strItem
: SelectedPanelItems
) {
1896 if (strTarget
== strItem
) {
1897 // fprintf(stderr, "%s('%ls'): TRUE, '%ls' matches '%ls'\n", __FUNCTION__, SymLink, strTarget.CPtr(), strItem.CPtr());
1900 FARString strItemReal
;
1901 ConvertNameToReal(strItem
, strItemReal
);
1903 if (strTarget
.GetLength() > strItemReal
.GetLength()
1904 && strTarget
[strItemReal
.GetLength()] == GOOD_SLASH
&& strTarget
.Begins(strItemReal
)) {
1905 // fprintf(stderr, "%s('%ls'): TRUE, '%ls' under '%ls'\n", __FUNCTION__, SymLink, strTarget.CPtr(), strItemReal.CPtr());
1910 // fprintf(stderr, "IsSymlinkTargetAlsoCopied('%ls'): FALSE, '%ls'\n", SymLink, strTarget.CPtr());
1915 ShellCopy::CreateSymLink(const char *Target
, const wchar_t *NewName
, const FAR_FIND_DATA_EX
&SrcData
)
1917 if (apiIsDevNull(NewName
))
1918 return COPY_SUCCESS
;
1920 int r
= sdc_symlink(Target
, Wide2MB(NewName
).c_str());
1922 return COPY_SUCCESS
;
1924 if (errno
== EEXIST
) {
1925 int RetCode
= 0, Append
= 0;
1926 FARString strNewName
= NewName
, strTarget
= Target
;
1927 if (AskOverwrite(SrcData
, strTarget
, NewName
, 0, 0, 0, 0, Append
, strNewName
, RetCode
)) {
1928 if (strNewName
== NewName
) {
1929 fprintf(stderr
, "CreateSymLink('%s', '%ls') - overwriting and strNewName='%ls'\n", Target
,
1930 NewName
, strNewName
.CPtr());
1931 sdc_remove(strNewName
.GetMB().c_str());
1934 fprintf(stderr
, "CreateSymLink('%s', '%ls') - renaming and strNewName='%ls'\n", Target
,
1935 NewName
, strNewName
.CPtr());
1937 return CreateSymLink(Target
, strNewName
.CPtr(), SrcData
);
1940 return (COPY_CODES
)RetCode
;
1943 switch (Message(MSG_WARNING
, 3, Msg::Error
, Msg::CopyCannotCreateSymlinkAskCopyContents
, NewName
,
1944 Msg::Yes
, Msg::Skip
, Msg::Cancel
)) {
1946 Flags
.SYMLINK
= COPY_SYMLINK_ASFILE
;
1950 return COPY_FAILURE
;
1958 static bool InSameDirectory(const wchar_t *ExistingName
, const wchar_t *NewName
) // #1334
1960 FARString strExistingDir
, strNewDir
;
1961 ConvertNameToFull(ExistingName
, strExistingDir
);
1962 ConvertNameToFull(NewName
, strNewDir
);
1963 CutToSlash(strExistingDir
);
1964 CutToSlash(strNewDir
);
1965 return strExistingDir
== strNewDir
;
1969 ShellCopy::CopySymLink(const wchar_t *ExistingName
, const wchar_t *NewName
, const FAR_FIND_DATA_EX
&SrcData
)
1971 FARString strExistingName
;
1972 ConvertNameToFull(ExistingName
, strExistingName
);
1974 // fprintf(stderr, "CopySymLink('%ls', '%ls', '%ls') '%ls'\n", Root, ExistingName, NewName, strRealName.CPtr());
1975 const std::string
&mbExistingName
= strExistingName
.GetMB();
1976 char LinkTarget
[PATH_MAX
+ 1];
1977 ssize_t r
= sdc_readlink(mbExistingName
.c_str(), LinkTarget
, sizeof(LinkTarget
) - 1);
1978 if (r
<= 0 || r
>= (ssize_t
)sizeof(LinkTarget
) || LinkTarget
[0] == 0) {
1979 fprintf(stderr
, "CopySymLink: r=%ld errno=%u from sdc_readlink('%ls')\n", (long)r
, errno
,
1980 strExistingName
.CPtr());
1981 return COPY_FAILURE
;
1987 create exactly same symlink as existing one in following cases:
1988 - if settings specifies to not be smart
1989 - if existing symlink is relative
1990 - if existing symlink points to unexisting destination that is also out or set of files being copied
1991 note that in case of being smart and if symlink is relative then caller
1992 guarantees that its target is within copied tree, so link will be valid
1994 if (Flags
.SYMLINK
!= COPY_SYMLINK_SMART
1995 || (LinkTarget
[0] != GOOD_SLASH
&& !InSameDirectory(ExistingName
, NewName
))
1996 || ((SrcData
.dwFileAttributes
& FILE_ATTRIBUTE_BROKEN
) != 0
1997 && !IsSymlinkTargetAlsoCopied(ExistingName
))) {
1998 FARString strNewName
;
1999 ConvertNameToFull(NewName
, strNewName
);
2000 return CreateSymLink(LinkTarget
, strNewName
.CPtr(), SrcData
);
2004 this is a case of smart linking - create symlink that relatively points to _new_ target
2005 by applying to new link relative path from existing symlink to its existing target
2007 FARString strRealName
;
2008 ConvertNameToReal(ExistingName
, strRealName
);
2009 std::vector
<std::string
> partsRealName
, partsExistingName
;
2010 StrExplode(partsRealName
, strRealName
.GetMB(), "/");
2011 StrExplode(partsExistingName
, strExistingName
.GetMB(), "/");
2013 size_t common_anchestors_count
= 0;
2014 while (common_anchestors_count
< partsRealName
.size()
2015 && common_anchestors_count
< partsExistingName
.size()
2016 && partsRealName
[common_anchestors_count
] == partsExistingName
[common_anchestors_count
]) {
2017 ++common_anchestors_count
;
2020 std::string relative_target
;
2021 for (size_t i
= common_anchestors_count
; i
+ 1 < partsExistingName
.size(); ++i
) {
2022 relative_target
+= "../";
2024 for (size_t i
= common_anchestors_count
; i
< partsRealName
.size(); ++i
) {
2025 relative_target
+= partsRealName
[i
];
2026 if (i
+ 1 < partsRealName
.size()) {
2027 relative_target
+= GOOD_SLASH
;
2030 if (relative_target
.empty()) {
2031 fprintf(stderr
, "CopySymLink: empty relative_target strRealName='%ls' strExistingName='%ls'\n",
2032 strRealName
.CPtr(), strExistingName
.CPtr());
2035 return CreateSymLink(relative_target
.c_str(), NewName
, SrcData
);
2038 COPY_CODES
ShellCopy::ShellCopyOneFile(const wchar_t *Src
, const FAR_FIND_DATA_EX
&SrcData
,
2039 FARString
&strDest
, int KeepPathPos
, int Rename
)
2042 COPY_CODES out
= ShellCopyOneFileNoRetry(Src
, SrcData
, strDest
, KeepPathPos
, Rename
);
2043 if (out
!= COPY_RETRY
)
2048 COPY_CODES
ShellCopy::ShellCopyOneFileNoRetry(const wchar_t *Src
, const FAR_FIND_DATA_EX
&SrcData
,
2049 FARString
&strDest
, int KeepPathPos
, int Rename
)
2051 CurCopiedSize
= 0; // Сбросить текущий прогресс
2053 if (CP
->Cancelled()) {
2054 return (COPY_CANCEL
);
2058 if (!Filter
->FileInFilter(SrcData
))
2059 return COPY_NOFILTER
;
2062 FARString strDestPath
= strDest
;
2063 const wchar_t *NamePtr
= PointToName(strDestPath
);
2064 DWORD DestAttr
= (strDestPath
== WGOOD_SLASH
|| !*NamePtr
|| TestParentFolderName(NamePtr
))
2065 ? FILE_ATTRIBUTE_DIRECTORY
2066 : INVALID_FILE_ATTRIBUTES
;
2068 FAR_FIND_DATA_EX DestData
;
2070 if (DestAttr
== INVALID_FILE_ATTRIBUTES
) {
2071 if (apiGetFindDataForExactPathName(strDestPath
, DestData
))
2072 DestAttr
= DestData
.dwFileAttributes
;
2075 int SameName
= 0, Append
= 0;
2077 if (DestAttr
!= INVALID_FILE_ATTRIBUTES
&& (DestAttr
& FILE_ATTRIBUTE_DIRECTORY
)) {
2078 int CmpCode
= CmpFullNames(Src
, strDestPath
);
2080 if (CmpCode
&& SrcData
.dwFileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
&& RPT
== RP_EXACTCOPY
2081 && Flags
.SYMLINK
!= COPY_SYMLINK_ASFILE
) {
2085 if (CmpCode
== 1) // TODO: error check
2090 CmpCode
= !StrCmp(PointToName(Src
), PointToName(strDestPath
));
2094 SetMessageHelp(L
"ErrCopyItSelf");
2095 Message(MSG_WARNING
, 1, Msg::Error
, Msg::CannotCopyFolderToItself1
, Src
,
2096 Msg::CannotCopyFolderToItself2
, Msg::Ok
);
2097 return (COPY_CANCEL
);
2102 int Length
= (int)strDestPath
.GetLength();
2104 if (!IsSlash(strDestPath
.At(Length
- 1)) && strDestPath
.At(Length
- 1) != L
':')
2105 strDestPath
+= WGOOD_SLASH
;
2107 const wchar_t *PathPtr
= Src
+ KeepPathPos
;
2109 if (*PathPtr
&& !KeepPathPos
&& PathPtr
[1] == L
':')
2112 if (IsSlash(*PathPtr
))
2115 strDestPath
+= PathPtr
;
2117 if (!apiGetFindDataForExactPathName(strDestPath
, DestData
))
2118 DestAttr
= INVALID_FILE_ATTRIBUTES
;
2120 DestAttr
= DestData
.dwFileAttributes
;
2124 SetDestDizPath(strDestPath
);
2126 CP
->SetProgressValue(0, 0);
2127 CP
->SetNames(Src
, strDestPath
);
2129 const bool copy_sym_link
= (RPT
== RP_EXACTCOPY
2130 && (SrcData
.dwFileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
) != 0
2131 && ((SrcData
.dwFileAttributes
& FILE_ATTRIBUTE_BROKEN
) != 0
2132 || (Flags
.SYMLINK
!= COPY_SYMLINK_ASFILE
2133 && (Flags
.SYMLINK
!= COPY_SYMLINK_SMART
|| IsSymlinkTargetAlsoCopied(Src
)
2134 || InSameDirectory(Src
, strDestPath
)))));
2136 if ((SrcData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) != 0 || copy_sym_link
) {
2138 strCopiedName
= PointToName(strDestPath
);
2140 if (DestAttr
!= INVALID_FILE_ATTRIBUTES
) {
2141 if ((DestAttr
& FILE_ATTRIBUTE_DIRECTORY
) && !SameName
) {
2142 FARString strSrcFullName
;
2143 ConvertNameToFull(Src
, strSrcFullName
);
2144 return (!StrCmp(strDestPath
, strSrcFullName
) ? COPY_NEXT
: COPY_SUCCESS
);
2147 int Type
= apiGetFileTypeByName(strDestPath
);
2149 if (Type
== FILE_TYPE_CHAR
|| Type
== FILE_TYPE_PIPE
)
2150 return (Rename
? COPY_NEXT
: COPY_SUCCESS
);
2153 if ((SrcData
.dwFileAttributes
& (FILE_ATTRIBUTE_REPARSE_POINT
| FILE_ATTRIBUTE_DIRECTORY
))
2154 == FILE_ATTRIBUTE_DIRECTORY
) {
2156 Enqueue attributes before creating directory, so even if will fail (like directory exists)
2157 but ignored then still will still try apply them on whole copy process finish successfully
2159 EnqueueDirectoryAttributes(SrcData
, strDestPath
);
2163 FARString strSrcFullName
, strDestFullName
;
2164 ConvertNameToFull(Src
, strSrcFullName
);
2166 // Пытаемся переименовать, пока не отменят
2168 BOOL SuccessMove
= apiMoveFile(Src
, strDestPath
);
2171 if (PointToName(strDestPath
) == strDestPath
.CPtr())
2172 strRenamedName
= strDestPath
;
2174 strCopiedName
= PointToName(strDestPath
);
2176 ConvertNameToFull(strDest
, strDestFullName
);
2177 TreeList::RenTreeName(strSrcFullName
, strDestFullName
);
2178 return (SameName
? COPY_NEXT
: COPY_SUCCESS_MOVE
);
2180 int MsgCode
= Message(Flags
.ErrorMessageFlags
, 3, Msg::Error
,
2181 Msg::CopyCannotRenameFolder
, Src
, Msg::CopyRetry
, Msg::CopyIgnore
,
2188 if (apiCreateDirectory(strDestPath
, nullptr)) {
2189 if (PointToName(strDestPath
) == strDestPath
.CPtr())
2190 strRenamedName
= strDestPath
;
2192 strCopiedName
= PointToName(strDestPath
);
2194 TreeList::AddTreeName(strDestPath
);
2195 return (COPY_SUCCESS
);
2199 return (COPY_CANCEL
);
2204 if (RPT
!= RP_SYMLINKFILE
2205 && (SrcData
.dwFileAttributes
& (FILE_ATTRIBUTE_REPARSE_POINT
| FILE_ATTRIBUTE_DIRECTORY
))
2206 == FILE_ATTRIBUTE_DIRECTORY
) {
2207 while (!apiCreateDirectory(strDestPath
, nullptr)) {
2208 int MsgCode
= Message(Flags
.ErrorMessageFlags
, 3, Msg::Error
, Msg::CopyCannotCreateFolder
,
2209 strDestPath
, Msg::CopyRetry
, Msg::CopySkip
, Msg::CopyCancel
);
2212 return ((MsgCode
== -2 || MsgCode
== 2) ? COPY_CANCEL
: COPY_NEXT
);
2215 // [ ] Copy contents of symbolic links
2216 if (copy_sym_link
) {
2217 COPY_CODES CopyRetCode
= CopySymLink(Src
, strDestPath
, SrcData
);
2218 if (CopyRetCode
!= COPY_SUCCESS
&& CopyRetCode
!= COPY_SUCCESS_MOVE
)
2221 if (SrcData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
2222 TreeList::AddTreeName(strDestPath
);
2223 return COPY_SUCCESS
;
2226 if (DestAttr
!= INVALID_FILE_ATTRIBUTES
&& !(DestAttr
& FILE_ATTRIBUTE_DIRECTORY
)) {
2227 if (SrcData
.nFileSize
== DestData
.nFileSize
) {
2228 int CmpCode
= CmpFullNames(Src
, strDestPath
);
2230 if (CmpCode
&& SrcData
.dwFileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
&& RPT
== RP_EXACTCOPY
2231 && Flags
.SYMLINK
!= COPY_SYMLINK_ASFILE
) {
2235 if (CmpCode
== 1) // TODO: error check
2240 CmpCode
= !StrCmp(PointToName(Src
), PointToName(strDestPath
));
2243 if (CmpCode
== 1 && !Rename
) {
2244 Message(MSG_WARNING
, 1, Msg::Error
, Msg::CannotCopyFileToItself1
, Src
,
2245 Msg::CannotCopyFileToItself2
, Msg::Ok
);
2246 return (COPY_CANCEL
);
2251 FARString strNewName
;
2253 if (!AskOverwrite(SrcData
, Src
, strDestPath
, DestAttr
, SameName
, Rename
, (Flags
.LINK
? 0 : 1), Append
,
2254 strNewName
, RetCode
)) {
2255 return ((COPY_CODES
)RetCode
);
2258 if (RetCode
== COPY_RETRY
) {
2259 strDest
= strNewName
;
2261 if (CutToSlash(strNewName
) && apiGetFileAttributes(strNewName
) == INVALID_FILE_ATTRIBUTES
) {
2262 CreatePath(strNewName
);
2271 uint64_t SaveTotalSize
= TotalCopiedSize
;
2274 int MoveCode
= FALSE
, AskDelete
;
2277 FARString strSrcFullName
;
2278 ConvertNameToFull(Src
, strSrcFullName
);
2280 MoveCode
= apiMoveFileEx(strSrcFullName
, strDestPath
,
2281 SameName
? MOVEFILE_COPY_ALLOWED
: MOVEFILE_COPY_ALLOWED
| MOVEFILE_REPLACE_EXISTING
);
2283 if (!MoveCode
&& WINPORT(GetLastError
)() == ERROR_NOT_SAME_DEVICE
) {
2284 return COPY_FAILURE
;
2287 if (ShowTotalCopySize
&& MoveCode
) {
2288 TotalCopiedSize
+= SrcData
.nFileSize
;
2289 CP
->SetTotalProgressValue(TotalCopiedSize
, TotalCopySize
);
2295 CopyCode
= ShellCopyFile(Src
, SrcData
, strDestPath
, Append
);
2296 } while (CopyCode
== COPY_RETRY
);
2302 case COPY_FAILUREREAD
:
2316 if (DestAttr
== INVALID_FILE_ATTRIBUTES
|| !(DestAttr
& FILE_ATTRIBUTE_DIRECTORY
)) {
2317 if (PointToName(strDestPath
) == strDestPath
.CPtr())
2318 strRenamedName
= strDestPath
;
2320 strCopiedName
= PointToName(strDestPath
);
2325 if (AskDelete
&& DeleteAfterMove(Src
, SrcData
.dwFileAttributes
) == COPY_CANCEL
)
2328 return (COPY_SUCCESS_MOVE
);
2332 CopyCode
= ShellCopyFile(Src
, SrcData
, strDestPath
, Append
);
2333 } while (CopyCode
== COPY_RETRY
);
2335 if (Append
&& DestData
.dwUnixMode
!= 0
2336 && (CopyCode
!= COPY_SUCCESS
|| DestData
.dwUnixMode
!= SrcData
.dwUnixMode
)) {
2337 const std::string
&mbDestPath
= strDestPath
.GetMB();
2338 sdc_chmod(mbDestPath
.c_str(), DestData
.dwUnixMode
);
2340 if (CopyCode
== COPY_SUCCESS
) {
2341 strCopiedName
= PointToName(strDestPath
);
2343 return COPY_SUCCESS
;
2344 } else if (CopyCode
== COPY_CANCEL
|| CopyCode
== COPY_NEXT
) {
2345 return ((COPY_CODES
)CopyCode
);
2350 if (CopyCode
== COPY_FAILUREREAD
)
2351 return COPY_FAILURE
;
2354 FARString strMsg1
, strMsg2
;
2355 FarLangMsg MsgMCannot
= Flags
.LINK
? Msg::CannotLink
: Flags
.MOVE
? Msg::CannotMove
: Msg::CannotCopy
;
2357 strMsg2
= strDestPath
;
2358 InsertQuote(strMsg1
);
2359 InsertQuote(strMsg2
);
2366 MsgCode
= Message(Flags
.ErrorMessageFlags
, 4, Msg::Error
, MsgMCannot
, strMsg1
,
2367 Msg::CannotCopyTo
, strMsg2
, Msg::CopyRetry
, Msg::CopySkip
, Msg::CopySkipAll
,
2383 TotalCopiedSize
= SaveTotalSize
;
2385 FARString strNewName
;
2387 if (!AskOverwrite(SrcData
, Src
, strDestPath
, DestAttr
, SameName
, Rename
, (Flags
.LINK
? 0 : 1), Append
,
2388 strNewName
, RetCode
))
2389 return ((COPY_CODES
)RetCode
);
2391 if (RetCode
== COPY_RETRY
) {
2392 strDest
= strNewName
;
2394 if (CutToSlash(strNewName
) && apiGetFileAttributes(strNewName
) == INVALID_FILE_ATTRIBUTES
) {
2395 CreatePath(strNewName
);
2403 int ShellCopy::DeleteAfterMove(const wchar_t *Name
, DWORD Attr
)
2405 if (Attr
& FILE_ATTRIBUTE_READONLY
) {
2408 if (!Opt
.Confirm
.RO
)
2409 ReadOnlyDelMode
= 1;
2411 if (ReadOnlyDelMode
!= -1)
2412 MsgCode
= ReadOnlyDelMode
;
2414 MsgCode
= Message(MSG_WARNING
, 5, Msg::Warning
, Msg::CopyFileRO
, Name
, Msg::CopyAskDelete
,
2415 Msg::CopyDeleteRO
, Msg::CopyDeleteAllRO
, Msg::CopySkipRO
, Msg::CopySkipAllRO
,
2420 ReadOnlyDelMode
= 1;
2425 ReadOnlyDelMode
= 3;
2430 return (COPY_CANCEL
);
2434 TemporaryMakeWritable
tmw(Name
);
2436 while ((Attr
& FILE_ATTRIBUTE_DIRECTORY
) ? !apiRemoveDirectory(Name
) : !apiDeleteFile(Name
)) {
2439 if (SkipDeleteMode
!= -1)
2440 MsgCode
= SkipDeleteMode
;
2442 MsgCode
= Message(Flags
.ErrorMessageFlags
, 4, Msg::Error
, Msg::CannotDeleteFile
, Name
,
2443 Msg::DeleteRetry
, Msg::DeleteSkip
, Msg::DeleteSkipAll
, Msg::DeleteCancel
);
2454 return (COPY_CANCEL
);
2458 return (COPY_SUCCESS
);
2461 static void ProgressUpdate(bool force
, const FAR_FIND_DATA_EX
&SrcData
, const wchar_t *DestName
)
2463 if (force
|| GetProcessUptimeMSec() - ProgressUpdateTime
>= PROGRESS_REFRESH_THRESHOLD
) {
2464 CP
->SetProgressValue(CurCopiedSize
, SrcData
.nFileSize
);
2466 if (ShowTotalCopySize
) {
2467 CP
->SetTotalProgressValue(TotalCopiedSize
, TotalCopySize
);
2470 CP
->SetNames(SrcData
.strFileName
, DestName
);
2472 ProgressUpdateTime
= GetProcessUptimeMSec();
2476 /////////////////////////////////////////////////////////// BEGIN OF ShellFileTransfer
2478 ShellFileTransfer::ShellFileTransfer(const wchar_t *SrcName
, const FAR_FIND_DATA_EX
&SrcData
,
2479 const FARString
&strDestName
, bool Append
, ShellCopyBuffer
&CopyBuffer
, COPY_FLAGS
&Flags
)
2481 _SrcName(SrcName
), _strDestName(strDestName
), _CopyBuffer(CopyBuffer
), _Flags(Flags
), _SrcData(SrcData
)
2483 if (!_SrcFile
.Open(SrcName
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, nullptr,
2484 OPEN_EXISTING
, FILE_FLAG_SEQUENTIAL_SCAN
))
2487 if (_Flags
.COPYXATTR
) {
2488 _XAttrCopyPtr
.reset(new ShellCopyFileExtendedAttributes(_SrcFile
));
2491 if (_Flags
.COPYACCESSMODE
) { // force S_IWUSR for a while file being copied, it will be removed afterwards if not needed
2492 _ModeToCreateWith
= _SrcData
.dwUnixMode
| S_IWUSR
;
2495 _DstFlags
= FILE_FLAG_SEQUENTIAL_SCAN
;
2496 if (Flags
.WRITETHROUGH
) {
2497 _DstFlags
|= FILE_FLAG_WRITE_THROUGH
;
2499 #ifdef __linux__ // anyway OSX doesn't have O_DIRECT
2500 if (SrcData
.nFileSize
> 32 * USE_PAGE_SIZE
) // just empiric
2501 _DstFlags
|= FILE_FLAG_NO_BUFFERING
;
2505 bool DstOpened
= _DestFile
.Open(_strDestName
, GENERIC_WRITE
, FILE_SHARE_READ
,
2506 _Flags
.COPYACCESSMODE
? &_ModeToCreateWith
: nullptr, (Append
? OPEN_EXISTING
: CREATE_ALWAYS
),
2509 if ((_DstFlags
& (FILE_FLAG_WRITE_THROUGH
| FILE_FLAG_NO_BUFFERING
)) != 0) {
2511 _DstFlags
&= ~(FILE_FLAG_WRITE_THROUGH
| FILE_FLAG_NO_BUFFERING
);
2512 DstOpened
= _DestFile
.Open(_strDestName
, GENERIC_WRITE
, FILE_SHARE_READ
,
2513 _Flags
.COPYACCESSMODE
? &_ModeToCreateWith
: nullptr,
2514 (Append
? OPEN_EXISTING
: CREATE_ALWAYS
), _DstFlags
);
2516 Flags
.WRITETHROUGH
= false;
2517 fprintf(stderr
, "COPY: unbuffered FAILED: 0x%x\n",
2518 _DstFlags
& (FILE_FLAG_WRITE_THROUGH
| FILE_FLAG_NO_BUFFERING
));
2521 fprintf(stderr, "COPY: unbuffered OK: 0x%x\n", DstFlags & (FILE_FLAG_WRITE_THROUGH|FILE_FLAG_NO_BUFFERING));*/
2527 _LOGCOPYR(SysLog(L
"return COPY_FAILURE -> %d CreateFile=-1, LastError=%d (0x%08X)", __LINE__
,
2528 _localLastError
, _localLastError
));
2534 if (!_DestFile
.SetPointer(0, &_AppendPos
, FILE_END
)) {
2541 } else if (SrcData
.nFileSize
> (uint64_t)_CopyBuffer
.Size
&& !_Flags
.SPARSEFILES
&& !_Flags
.USECOW
) {
2542 _DestFile
.AllocationHint(SrcData
.nFileSize
);
2546 ShellFileTransfer::~ShellFileTransfer()
2550 fprintf(stderr
, "~ShellFileTransfer: discarding '%ls'\n", _strDestName
.CPtr());
2552 CP
->SetProgressValue(0, 0);
2553 CurCopiedSize
= 0; // Сбросить текущий прогресс
2555 if (_AppendPos
!= -1) {
2556 _DestFile
.SetPointer(_AppendPos
, nullptr, FILE_BEGIN
);
2562 if (_AppendPos
== -1) {
2563 TemporaryMakeWritable
tmw(_strDestName
);
2564 apiDeleteFile(_strDestName
);
2567 ProgressUpdate(true, _SrcData
, _strDestName
);
2568 } catch (std::exception
&ex
) {
2569 fprintf(stderr
, "~ShellFileTransfer: %s\n", ex
.what());
2571 fprintf(stderr
, "~ShellFileTransfer: ...\n");
2575 void ShellFileTransfer::Do()
2577 CP
->SetProgressValue(0, 0);
2580 ProgressUpdate(false, _SrcData
, _strDestName
);
2582 if (OrigScrX
!= ScrX
|| OrigScrY
!= ScrY
) {
2588 if (CP
->Cancelled())
2591 _Stopwatch
= (_SrcData
.nFileSize
- CurCopiedSize
> (uint64_t)_CopyBuffer
.Size
)
2592 ? GetProcessUptimeMSec()
2595 DWORD BytesWritten
= PieceCopy();
2596 if (BytesWritten
== 0)
2599 CurCopiedSize
+= BytesWritten
;
2601 if (_Stopwatch
!= 0 && BytesWritten
== _CopyBuffer
.Size
) {
2602 _Stopwatch
= GetProcessUptimeMSec() - _Stopwatch
;
2603 if (_Stopwatch
< 100) {
2604 if (_CopyBuffer
.Size
< _CopyBuffer
.Capacity
) {
2605 _CopyBuffer
.Size
= std::min(_CopyBuffer
.Size
* 2, _CopyBuffer
.Capacity
);
2606 fprintf(stderr
, "CopyPieceSize increased to %d\n", _CopyBuffer
.Size
);
2608 } else if (_Stopwatch
>= 1000 && _CopyBuffer
.Size
> (int)COPY_PIECE_MINIMAL
) {
2609 _CopyBuffer
.Size
= std::max(_CopyBuffer
.Size
/ 2, (DWORD
)COPY_PIECE_MINIMAL
);
2610 fprintf(stderr
, "CopyPieceSize decreased to %d\n", _CopyBuffer
.Size
);
2614 if (ShowTotalCopySize
)
2615 TotalCopiedSize
+= BytesWritten
;
2620 if (!apiIsDevNull(_strDestName
)) // avoid sudo prompt when copying to /dev/null
2622 if (_LastWriteWasHole
) {
2623 while (!_DestFile
.SetEnd()) {
2624 RetryCancel(Msg::CopyWriteError
, _strDestName
);
2629 _XAttrCopyPtr
->ApplyToCopied(_DestFile
);
2631 if (_Flags
.COPYACCESSMODE
2632 && (_ModeToCreateWith
!= _SrcData
.dwUnixMode
|| (g_umask
& _SrcData
.dwUnixMode
) != 0))
2633 _DestFile
.Chmod(_SrcData
.dwUnixMode
);
2635 _DestFile
.SetTime(nullptr, nullptr, &_SrcData
.ftLastWriteTime
, nullptr);
2638 if (!_DestFile
.Close()) {
2641 if file located on old samba share then in out of space condition
2642 write()-s succeed but close() reports error
2649 ProgressUpdate(false, _SrcData
, _strDestName
);
2652 void ShellFileTransfer::RetryCancel(const wchar_t *Text
, const wchar_t *Object
)
2655 _Stopwatch
= 0; // UI messes timings
2657 Message(_Flags
.ErrorMessageFlags
, 2, Msg::Error
, Text
, Object
, Msg::Retry
, Msg::Cancel
);
2665 // returns std:::pair<OffsetOfNextHole, SizeOfNextHole> (SizeOfNextHole==0 means no holes found)
2666 static std::pair
<DWORD
, DWORD
> LookupNextHole(const unsigned char *Data
, DWORD Size
, uint64_t Offset
)
2668 const DWORD Alignment
= 0x1000; // must be power of 2
2669 const DWORD OffsetMisalignment
= DWORD(Offset
) & (Alignment
- 1);
2672 if (OffsetMisalignment
) {
2673 i
+= Alignment
- OffsetMisalignment
;
2676 for (; i
< Size
; i
+= Alignment
) {
2677 DWORD ZeroesCount
= 0;
2678 while (i
+ ZeroesCount
< Size
&& Data
[i
+ ZeroesCount
] == 0) {
2681 if (ZeroesCount
>= Alignment
) {
2682 return std::make_pair(i
, (ZeroesCount
/ Alignment
) * Alignment
);
2686 return std::make_pair(Size
, (DWORD
)0);
2689 DWORD
ShellFileTransfer::PieceCopy()
2691 #if defined(COW_SUPPORTED) && defined(__linux__)
2694 ssize_t sz
= copy_file_range(_SrcFile
.Descriptor(), nullptr, _DestFile
.Descriptor(), nullptr,
2695 _CopyBuffer
.Size
, 0);
2700 if (errno
== EXDEV
) {
2701 fprintf(stderr
, "copy_file_range returned EXDEV, fallback to usual copy\n");
2705 RetryCancel(Msg::CopyWriteError
, _strDestName
);
2709 DWORD BytesRead
, BytesWritten
;
2711 while (!_SrcFile
.Read(_CopyBuffer
.Ptr
, _CopyBuffer
.Size
, &BytesRead
)) {
2712 RetryCancel(Msg::CopyReadError
, _SrcName
);
2718 DWORD WriteSize
= BytesRead
;
2719 if ((_DstFlags
& FILE_FLAG_NO_BUFFERING
) != 0)
2720 WriteSize
= AlignPageUp(WriteSize
);
2723 if (_Flags
.SPARSEFILES
) {
2724 while (BytesWritten
< WriteSize
) {
2725 const unsigned char *Data
= (const unsigned char *)_CopyBuffer
.Ptr
+ BytesWritten
;
2726 const std::pair
<DWORD
, DWORD
> &NH
=
2727 LookupNextHole(Data
, WriteSize
- BytesWritten
, CurCopiedSize
+ BytesWritten
);
2728 DWORD LeadingNonzeroesWritten
= NH
.first
? PieceWrite(Data
, NH
.first
) : 0;
2729 BytesWritten
+= LeadingNonzeroesWritten
;
2730 if (NH
.second
&& LeadingNonzeroesWritten
== NH
.first
) {
2731 // fprintf(stderr, "!!! HOLE of size %x\n", SR.second);
2732 BytesWritten
+= PieceWriteHole(NH
.second
);
2736 BytesWritten
= PieceWrite(_CopyBuffer
.Ptr
, WriteSize
);
2738 if (BytesWritten
> BytesRead
) {
2740 likely we written bit more due to no_buffering requires aligned io
2741 move backward and correct file size
2743 if (!_DestFile
.SetPointer((INT64
)BytesRead
- (INT64
)WriteSize
, nullptr, FILE_CURRENT
))
2745 if (!_DestFile
.SetEnd())
2750 if (BytesWritten
< BytesRead
) { // if written less than read then need to rewind source file by difference
2751 if (!_SrcFile
.SetPointer((INT64
)BytesWritten
- (INT64
)BytesRead
, nullptr, FILE_CURRENT
))
2755 return BytesWritten
;
2758 DWORD
ShellFileTransfer::PieceWriteHole(DWORD Size
)
2760 while (!_DestFile
.SetPointer(Size
, nullptr, FILE_CURRENT
)) {
2761 RetryCancel(Msg::CopyWriteError
, _strDestName
);
2763 _LastWriteWasHole
= true;
2767 DWORD
ShellFileTransfer::PieceWrite(const void *Data
, DWORD Size
)
2769 DWORD BytesWritten
= 0;
2770 while (!_DestFile
.Write(Data
, Size
, &BytesWritten
)) {
2771 RetryCancel(Msg::CopyWriteError
, _strDestName
);
2773 _LastWriteWasHole
= false;
2774 return BytesWritten
;
2777 /////////////////////////////////////////////////////////// END OF ShellFileTransfer
2779 static dev_t
GetRDev(FARString SrcName
)
2782 if (sdc_stat(SrcName
.GetMB().c_str(), &st
) == 0) {
2788 int ShellCopy::ShellCopyFile(const wchar_t *SrcName
, const FAR_FIND_DATA_EX
&SrcData
, FARString
&strDestName
,
2795 if (RPT
== RP_HARDLINK
) {
2796 apiDeleteFile(strDestName
); // BUGBUG
2797 return (MkHardLink(SrcName
, strDestName
) ? COPY_SUCCESS
: COPY_FAILURE
);
2799 return (MkSymLink(SrcName
, strDestName
, RPT
, true) ? COPY_SUCCESS
: COPY_FAILURE
);
2802 if (SrcData
.dwFileAttributes
& (FILE_ATTRIBUTE_DEVICE_FIFO
| FILE_ATTRIBUTE_DEVICE_BLOCK
| FILE_ATTRIBUTE_DEVICE_CHAR
)) {
2803 int r
= (SrcData
.dwFileAttributes
& FILE_ATTRIBUTE_DEVICE_FIFO
)
2804 ? sdc_mkfifo(strDestName
.GetMB().c_str(), SrcData
.dwUnixMode
)
2805 : sdc_mknod(strDestName
.GetMB().c_str(), SrcData
.dwUnixMode
, GetRDev(SrcName
));
2807 _localLastError
= errno
;
2808 return COPY_FAILURE
;
2810 return COPY_SUCCESS
;
2814 #if defined(COW_SUPPORTED) && defined(__APPLE__)
2816 const std::string mbSrc
= Wide2MB(SrcName
);
2817 const std::string
&mbDest
= strDestName
.GetMB();
2818 int r
= clonefile(mbSrc
.c_str(), mbDest
.c_str(), 0);
2820 // fprintf(stderr, "CoW succeeded for '%s' -> '%s'\n", mbSrc.c_str(), mbDest.c_str());
2821 CurCopiedSize
= SrcData
.nFileSize
;
2822 if (ShowTotalCopySize
)
2823 TotalCopiedSize
+= SrcData
.nFileSize
;
2825 ProgressUpdate(false, SrcData
, strDestName
);
2826 return CP
->Cancelled() ? COPY_CANCEL
: COPY_SUCCESS
;
2830 if (ErSr
.Get() != EXDEV
&& ErSr
.Get() != ENOTSUP
)
2833 fprintf(stderr
, "Skip CoW errno=%d for '%s' -> '%s'\n", ErSr
.Get(), mbSrc
.c_str(),
2838 ShellFileTransfer(SrcName
, SrcData
, strDestName
, Append
!= 0, CopyBuffer
, Flags
).Do();
2839 return CP
->Cancelled() ? COPY_CANCEL
: COPY_SUCCESS
;
2840 } catch (ErrnoSaver
&ErSr
) {
2841 _localLastError
= ErSr
.Get();
2844 return CP
->Cancelled() ? COPY_CANCEL
: COPY_FAILURE
;
2847 void ShellCopy::SetDestDizPath(const wchar_t *DestPath
)
2849 if (!Flags
.DIZREAD
) {
2850 ConvertNameToFull(DestPath
, strDestDizPath
);
2851 CutToSlash(strDestDizPath
);
2853 if (strDestDizPath
.IsEmpty())
2854 strDestDizPath
= L
".";
2856 if ((Opt
.Diz
.UpdateMode
== DIZ_UPDATE_IF_DISPLAYED
&& !SrcPanel
->IsDizDisplayed())
2857 || Opt
.Diz
.UpdateMode
== DIZ_NOT_UPDATE
)
2858 strDestDizPath
.Clear();
2860 if (!strDestDizPath
.IsEmpty())
2861 DestDiz
.Read(strDestDizPath
);
2863 Flags
.DIZREAD
= true;
2885 #define DM_OPENVIEWER DM_USER + 33
2887 LONG_PTR WINAPI
WarnDlgProc(HANDLE hDlg
, int Msg
, int Param1
, LONG_PTR Param2
)
2890 case DM_OPENVIEWER
: {
2891 LPCWSTR ViewName
= nullptr;
2892 FARString
**WFN
= reinterpret_cast<FARString
**>(SendDlgMessage(hDlg
, DM_GETDLGDATA
, 0, 0));
2896 case WDLG_SRCFILEBTN
:
2899 case WDLG_DSTFILEBTN
:
2905 // а этот трюк не даст пользователю сменить текущий каталог по CtrlF10 и этим ввести в заблуждение копир: TODODODO
2906 std::make_shared
<FileHolder
>(ViewName
, true),
2907 FALSE
, FALSE
, TRUE
, -1, nullptr, nullptr, FALSE
);
2908 Viewer
.SetDynamicallyBorn(FALSE
);
2909 FrameManager
->ExecuteModalEV();
2910 FrameManager
->ProcessKey(KEY_CONSOLE_BUFFER_RESIZE
);
2913 case DN_CTLCOLORDLGITEM
: {
2914 if (Param1
== WDLG_FILENAME
) {
2915 int Color
= FarColorToReal(COL_WARNDIALOGTEXT
) & 0xFF;
2916 return ((Param2
& 0xFF00FF00) | (Color
<< 16) | Color
);
2921 case WDLG_SRCFILEBTN
:
2922 case WDLG_DSTFILEBTN
:
2923 SendDlgMessage(hDlg
, DM_OPENVIEWER
, Param1
, 0);
2927 reinterpret_cast<FARString
**>(SendDlgMessage(hDlg
, DM_GETDLGDATA
, 0, 0));
2928 FARString strDestName
= *WFN
[1];
2929 GenerateName(strDestName
, *WFN
[2]);
2931 if (SendDlgMessage(hDlg
, DM_GETCHECK
, WDLG_CHECKBOX
, 0) == BSTATE_UNCHECKED
) {
2932 int All
= BSTATE_UNCHECKED
;
2934 if (GetString(Msg::CopyRenameTitle
, Msg::CopyRenameText
, nullptr, strDestName
,
2935 *WFN
[1], L
"CopyAskOverwrite",
2936 FIB_BUTTONS
| FIB_NOAMPERSAND
| FIB_EXPANDENV
| FIB_CHECKBOX
, &All
,
2937 Msg::CopyRememberChoice
)) {
2938 if (All
!= BSTATE_UNCHECKED
) {
2940 CutToSlash(*WFN
[2]);
2943 SendDlgMessage(hDlg
, DM_SETCHECK
, WDLG_CHECKBOX
, All
);
2948 *WFN
[1] = strDestName
;
2954 if ((Param1
== WDLG_SRCFILEBTN
|| Param1
== WDLG_DSTFILEBTN
) && Param2
== KEY_F3
) {
2955 SendDlgMessage(hDlg
, DM_OPENVIEWER
, Param1
, 0);
2960 return DefDlgProc(hDlg
, Msg
, Param1
, Param2
);
2963 int ShellCopy::AskOverwrite(const FAR_FIND_DATA_EX
&SrcData
, const wchar_t *SrcName
, const wchar_t *DestName
,
2964 DWORD DestAttr
, int SameName
, int Rename
, int AskAppend
, int &Append
, FARString
&strNewName
,
2969 WARN_DLG_HEIGHT
= 13,
2970 WARN_DLG_WIDTH
= 72,
2972 DialogDataEx WarnCopyDlgData
[] = {
2973 {DI_DOUBLEBOX
, 3, 1, WARN_DLG_WIDTH
- 4, WARN_DLG_HEIGHT
- 2, {}, 0, Msg::Warning
},
2974 {DI_TEXT
, 5, 2, WARN_DLG_WIDTH
- 6, 2, {}, DIF_CENTERTEXT
, Msg::CopyFileExist
},
2975 {DI_EDIT
, 5, 3, WARN_DLG_WIDTH
- 6, 3, {}, DIF_READONLY
, (wchar_t *)DestName
},
2976 {DI_TEXT
, 3, 4, 0, 4, {}, DIF_SEPARATOR
, L
""},
2977 {DI_BUTTON
, 5, 5, WARN_DLG_WIDTH
- 6, 5, {}, DIF_BTNNOCLOSE
| DIF_NOBRACKETS
, L
""},
2978 {DI_BUTTON
, 5, 6, WARN_DLG_WIDTH
- 6, 6, {}, DIF_BTNNOCLOSE
| DIF_NOBRACKETS
, L
""},
2979 {DI_TEXT
, 3, 7, 0, 7, {}, DIF_SEPARATOR
, L
""},
2980 {DI_CHECKBOX
, 5, 8, 0, 8, {}, DIF_FOCUS
, Msg::CopyRememberChoice
},
2981 {DI_TEXT
, 3, 9, 0, 9, {}, DIF_SEPARATOR
, L
""},
2982 {DI_BUTTON
, 0, 10, 0, 10, {}, DIF_DEFAULT
| DIF_CENTERGROUP
, Msg::CopyOverwrite
},
2983 {DI_BUTTON
, 0, 10, 0, 10, {}, DIF_CENTERGROUP
, Msg::CopySkipOvr
},
2984 {DI_BUTTON
, 0, 10, 0, 10, {}, DIF_CENTERGROUP
, Msg::CopyRename
},
2985 {DI_BUTTON
, 0, 10, 0, 10, {}, DIF_CENTERGROUP
| (AskAppend
? 0 : (DIF_DISABLE
| DIF_HIDDEN
)), Msg::CopyAppend
},
2986 {DI_BUTTON
, 0, 10, 0, 10, {}, DIF_CENTERGROUP
,Msg::CopyCancelOvr
}
2988 FAR_FIND_DATA_EX DestData
;
2990 int DestDataFilled
= FALSE
;
2993 if (DestAttr
== INVALID_FILE_ATTRIBUTES
)
2994 if ((DestAttr
= apiGetFileAttributes(DestName
)) == INVALID_FILE_ATTRIBUTES
)
2997 if (DestAttr
& FILE_ATTRIBUTE_DIRECTORY
)
3000 int MsgCode
= OvrMode
;
3001 FARString strDestName
= DestName
;
3003 if (OvrMode
== -1) {
3006 if ((!Opt
.Confirm
.Copy
&& !Rename
) || (!Opt
.Confirm
.Move
&& Rename
) || SameName
3007 || (Type
= apiGetFileTypeByName(DestName
)) == FILE_TYPE_CHAR
|| Type
== FILE_TYPE_PIPE
3008 || Flags
.OVERWRITENEXT
)
3012 apiGetFindDataForExactPathName(DestName
, DestData
);
3013 DestDataFilled
= TRUE
;
3015 if (Flags
.ONLYNEWERFILES
) {
3017 int64_t RetCompare
= FileTimeDifference(&DestData
.ftLastWriteTime
, &SrcData
.ftLastWriteTime
);
3024 FormatString strSrcFileStr
, strDestFileStr
;
3025 uint64_t SrcSize
= SrcData
.nFileSize
;
3026 FILETIME SrcLastWriteTime
= SrcData
.ftLastWriteTime
;
3027 if (Flags
.SYMLINK
== COPY_SYMLINK_ASFILE
3028 && (SrcData
.dwFileAttributes
& (FILE_ATTRIBUTE_REPARSE_POINT
| FILE_ATTRIBUTE_BROKEN
))
3029 == FILE_ATTRIBUTE_REPARSE_POINT
) {
3030 FARString RealName
= SrcName
;
3031 FAR_FIND_DATA_EX FindData
;
3032 apiGetFindDataForExactPathName(RealName
, FindData
);
3033 SrcSize
= FindData
.nFileSize
;
3034 SrcLastWriteTime
= FindData
.ftLastWriteTime
;
3036 FormatString strSrcSizeText
;
3037 strSrcSizeText
<< SrcSize
;
3038 uint64_t DestSize
= DestData
.nFileSize
;
3039 FormatString strDestSizeText
;
3040 strDestSizeText
<< DestSize
;
3041 FARString strDateText
, strTimeText
;
3042 ConvertDate(SrcLastWriteTime
, strDateText
, strTimeText
, 8, FALSE
, FALSE
, TRUE
, TRUE
);
3044 << fmt::Cells() << fmt::LeftAlign() << fmt::Expand(17) << Msg::CopySource
<< L
" "
3045 << fmt::Size(25) << strSrcSizeText
<< L
" " << strDateText
<< L
" " << strTimeText
;
3046 ConvertDate(DestData
.ftLastWriteTime
, strDateText
, strTimeText
, 8, FALSE
, FALSE
, TRUE
, TRUE
);
3047 strDestFileStr
<< fmt::Cells() << fmt::LeftAlign() << fmt::Expand(17) << Msg::CopyDest
<< L
" "
3048 << fmt::Size(25) << strDestSizeText
<< L
" " << strDateText
<< L
" " << strTimeText
;
3050 WarnCopyDlgData
[WDLG_SRCFILEBTN
].Data
= strSrcFileStr
;
3051 WarnCopyDlgData
[WDLG_DSTFILEBTN
].Data
= strDestFileStr
;
3052 MakeDialogItemsEx(WarnCopyDlgData
, WarnCopyDlg
);
3053 FARString strFullSrcName
;
3054 ConvertNameToFull(SrcName
, strFullSrcName
);
3055 FARString
*WFN
[] = {&strFullSrcName
, &strDestName
, &strRenamedFilesPath
};
3056 Dialog
WarnDlg(WarnCopyDlg
, ARRAYSIZE(WarnCopyDlg
), WarnDlgProc
, (LONG_PTR
)&WFN
);
3057 WarnDlg
.SetDialogMode(DMODE_WARNINGSTYLE
);
3058 WarnDlg
.SetPosition(-1, -1, WARN_DLG_WIDTH
, WARN_DLG_HEIGHT
);
3059 WarnDlg
.SetHelp(L
"CopyAskOverwrite");
3060 WarnDlg
.SetId(CopyOverwriteId
);
3063 switch (WarnDlg
.GetExitCode()) {
3064 case WDLG_OVERWRITE
:
3065 MsgCode
= WarnCopyDlg
[WDLG_CHECKBOX
].Selected
? 1 : 0;
3068 MsgCode
= WarnCopyDlg
[WDLG_CHECKBOX
].Selected
? 3 : 2;
3071 MsgCode
= WarnCopyDlg
[WDLG_CHECKBOX
].Selected
? 5 : 4;
3074 MsgCode
= WarnCopyDlg
[WDLG_CHECKBOX
].Selected
? 7 : 6;
3094 RetCode
= COPY_NEXT
;
3098 GenerateName(strDestName
, strRenamedFilesPath
);
3100 RetCode
= COPY_RETRY
;
3101 strNewName
= strDestName
;
3111 RetCode
= COPY_CANCEL
;
3115 if (RetCode
!= COPY_RETRY
) {
3116 if ((DestAttr
& FILE_ATTRIBUTE_READONLY
) && !Flags
.OVERWRITENEXT
) {
3120 if (ReadOnlyOvrMode
!= -1) {
3121 MsgCode
= ReadOnlyOvrMode
;
3123 if (!DestDataFilled
) {
3125 apiGetFindDataForExactPathName(DestName
, DestData
);
3128 FARString strDateText
, strTimeText
;
3129 FormatString strSrcFileStr
, strDestFileStr
;
3130 uint64_t SrcSize
= SrcData
.nFileSize
;
3131 FormatString strSrcSizeText
;
3132 strSrcSizeText
<< SrcSize
;
3133 uint64_t DestSize
= DestData
.nFileSize
;
3134 FormatString strDestSizeText
;
3135 strDestSizeText
<< DestSize
;
3136 ConvertDate(SrcData
.ftLastWriteTime
, strDateText
, strTimeText
, 8, FALSE
, FALSE
, TRUE
,
3139 << fmt::Cells() << fmt::LeftAlign() << fmt::Expand(17) << Msg::CopySource
<< L
" "
3140 << fmt::Size(25) << strSrcSizeText
<< L
" " << strDateText
<< L
" " << strTimeText
;
3141 ConvertDate(DestData
.ftLastWriteTime
, strDateText
, strTimeText
, 8, FALSE
, FALSE
, TRUE
,
3144 << fmt::Cells() << fmt::LeftAlign() << fmt::Expand(17) << Msg::CopyDest
<< L
" "
3145 << fmt::Size(25) << strDestSizeText
<< L
" " << strDateText
<< L
" " << strTimeText
;
3146 WarnCopyDlgData
[WDLG_SRCFILEBTN
].Data
= strSrcFileStr
;
3147 WarnCopyDlgData
[WDLG_DSTFILEBTN
].Data
= strDestFileStr
;
3148 WarnCopyDlgData
[WDLG_TEXT
].Data
= Msg::CopyFileRO
;
3149 WarnCopyDlgData
[WDLG_OVERWRITE
].Data
= (Append
? Msg::CopyAppend
: Msg::CopyOverwrite
);
3150 WarnCopyDlgData
[WDLG_RENAME
].Type
= DI_TEXT
;
3151 WarnCopyDlgData
[WDLG_RENAME
].Data
= L
"";
3152 WarnCopyDlgData
[WDLG_APPEND
].Type
= DI_TEXT
;
3153 WarnCopyDlgData
[WDLG_APPEND
].Data
= L
"";
3154 MakeDialogItemsEx(WarnCopyDlgData
, WarnCopyDlg
);
3155 FARString strSrcName
;
3156 ConvertNameToFull(SrcData
.strFileName
, strSrcName
);
3157 LPCWSTR WFN
[2] = {strSrcName
, DestName
};
3158 Dialog
WarnDlg(WarnCopyDlg
, ARRAYSIZE(WarnCopyDlg
), WarnDlgProc
, (LONG_PTR
)&WFN
);
3159 WarnDlg
.SetDialogMode(DMODE_WARNINGSTYLE
);
3160 WarnDlg
.SetPosition(-1, -1, WARN_DLG_WIDTH
, WARN_DLG_HEIGHT
);
3161 WarnDlg
.SetHelp(L
"CopyFiles");
3162 WarnDlg
.SetId(CopyReadOnlyId
);
3165 switch (WarnDlg
.GetExitCode()) {
3166 case WDLG_OVERWRITE
:
3167 MsgCode
= WarnCopyDlg
[WDLG_CHECKBOX
].Selected
? 1 : 0;
3170 MsgCode
= WarnCopyDlg
[WDLG_CHECKBOX
].Selected
? 3 : 2;
3183 ReadOnlyOvrMode
= 1;
3187 ReadOnlyOvrMode
= 2;
3189 RetCode
= COPY_NEXT
;
3194 RetCode
= COPY_CANCEL
;
3200 && (DestAttr
& (FILE_ATTRIBUTE_READONLY
| FILE_ATTRIBUTE_HIDDEN
| FILE_ATTRIBUTE_SYSTEM
)))
3201 apiMakeWritable(DestName
);
3207 BOOL
ShellCopySecuryMsg(const wchar_t *Name
)
3209 static clock_t PrepareSecuryStartTime
;
3212 || (static_cast<DWORD
>(GetProcessUptimeMSec() - PrepareSecuryStartTime
)
3213 > Opt
.ShowTimeoutDACLFiles
)) {
3214 static int Width
= 30;
3216 if (Name
&& *Name
) {
3217 PrepareSecuryStartTime
= GetProcessUptimeMSec(); // Первый файл рисуется всегда
3218 WidthTemp
= Max(StrLength(Name
), 30);
3220 Width
= WidthTemp
= 30;
3222 // ширина месага - 38%
3223 WidthTemp
= Min(WidthTemp
, WidthNameForMessage
);
3224 Width
= Max(Width
, WidthTemp
);
3226 FARString strOutFileName
= Name
; //??? nullptr ???
3227 TruncPathStr(strOutFileName
, Width
);
3228 CenterStr(strOutFileName
, strOutFileName
, Width
+ 4);
3229 Message(0, 0, Msg::MoveDlgTitle
, Msg::CopyPrepareSecury
, strOutFileName
);
3231 if (CP
->Cancelled()) {
3236 PreRedrawItem preRedrawItem
= PreRedraw
.Peek();
3237 preRedrawItem
.Param
.Param1
= Name
;
3238 PreRedraw
.SetParam(preRedrawItem
.Param
);
3242 bool ShellCopy::CalcTotalSize()
3244 FARString strSelName
;
3248 FAR_FIND_DATA_EX fd
;
3249 PreRedraw
.Push(PR_ShellCopyMsg
);
3250 PreRedrawItem preRedrawItem
= PreRedraw
.Peek();
3251 preRedrawItem
.Param
.Param1
= CP
;
3252 PreRedraw
.SetParam(preRedrawItem
.Param
);
3253 TotalCopySize
= CurCopiedSize
= 0;
3254 TotalFilesToProcess
= 0;
3255 SrcPanel
->GetSelNameCompat(nullptr, FileAttr
);
3257 while (SrcPanel
->GetSelNameCompat(&strSelName
, FileAttr
, &fd
)) {
3258 if ((FileAttr
& FILE_ATTRIBUTE_REPARSE_POINT
) && Flags
.SYMLINK
!= COPY_SYMLINK_ASFILE
)
3261 if (FileAttr
& FILE_ATTRIBUTE_DIRECTORY
) {
3263 uint32_t DirCount
, FileCount
, ClusterSize
;
3264 uint64_t PhysicalSize
;
3265 CP
->SetScanName(strSelName
);
3266 int __Ret
= GetDirInfo(L
"", strSelName
, DirCount
, FileCount
, FileSize
, PhysicalSize
,
3267 ClusterSize
, -1, Filter
,
3268 ((Flags
.SYMLINK
== COPY_SYMLINK_ASFILE
) ? GETDIRINFO_SCANSYMLINK
: 0)
3269 | (UseFilter
? GETDIRINFO_USEFILTER
: 0));
3272 ShowTotalCopySize
= false;
3277 if (FileCount
> 0) {
3278 TotalCopySize
+= FileSize
;
3279 TotalFilesToProcess
+= FileCount
;
3283 // Подсчитаем количество файлов
3285 if (!Filter
->FileInFilter(fd
))
3289 FileSize
= SrcPanel
->GetLastSelectedSize();
3291 if (FileSize
!= (uint64_t)-1) {
3292 TotalCopySize
+= FileSize
;
3293 TotalFilesToProcess
++;
3298 // INFO: Это для варианта, когда "ВСЕГО = общий размер * количество целей"
3299 TotalCopySize
= TotalCopySize
* CountTarget
;
3300 InsertCommas(TotalCopySize
, strTotalCopySizeText
);