1 /*
2 * Part of WCM Commander
3 * https://github.com/corporateshark/WCMCommander
4 * wcm@linderdaum.com
5 */
6
7 #ifdef _WIN32
8 # include <winsock2.h>
9 #endif
10
11 #include <time.h>
12 #include "fileopers.h"
13 #include "smblogon.h"
14 #include "ftplogon.h"
15 #include "sftpdlg.h"
16 #include "ltext.h"
17 #include "vfs-tmp.h"
18
19 enum
20 {
21 CMD_ALL = 200,
22 CMD_SKIP,
23 CMD_SKIPALL,
24 CMD_RETRY
25 };
26
27 #define MkDirMode (S_IRWXU | S_IRGRP | S_IROTH | S_IXGRP | S_IXOTH)
28
29 /*
30 //for ltext searcher
31 _LT("DB>Delete")
32 _LT("DB>All")
33 _LT("DB>Skip")
34 _LT("DB>Retry")
35 _LT("DB>Skip All")
36 _LT("DB>All")
37 _LT("DB>Yes")
38 _LT("DB>No")
39 */
40
41 static ButtonDataNode bDeleteAllSkipCancel[] = { {"&Delete", CMD_OK}, { "&All", CMD_ALL}, { "&Skip", CMD_SKIP}, {"&Cancel", CMD_CANCEL}, {0, 0}};
42 static ButtonDataNode bRetrySkipCancel[] = { { "&Retry", CMD_RETRY}, { "&Skip", CMD_SKIP}, {"&Cancel", CMD_CANCEL}, {0, 0}};
43 static ButtonDataNode bRetrySkipSkipallCancel[] = { { "&Retry", CMD_RETRY}, { "&Skip", CMD_SKIP}, { "Skip &All", CMD_SKIPALL}, {"&Cancel", CMD_CANCEL}, {0, 0}};
44 static ButtonDataNode bOkSkipCancel[] = { { "O&k", CMD_OK}, { "&Skip", CMD_SKIP}, {"&Cancel", CMD_CANCEL}, {0, 0}};
45 static ButtonDataNode bSkipCancel[] = { { "&Skip" , CMD_SKIP}, {"&Cancel", CMD_CANCEL}, {0, 0}};
46 static ButtonDataNode bSkipSkipallCancel[] = { { "&Skip", CMD_SKIP}, { "Skip &All", CMD_SKIPALL}, {"&Cancel", CMD_CANCEL}, {0, 0}};
47 //static ButtonDataNode bOk[] = { { " O&k ", CMD_OK}, {0,0}};
48 static ButtonDataNode bOkAllNoCancel[] = { { "O&k", CMD_OK}, { "&All", CMD_ALL}, { "&No", CMD_NO}, {"&Cancel", CMD_CANCEL}, {0, 0}};
49
50
51 ///////////////////////// smb callback objs
52
53 struct SmbLogonCBData
54 {
55 FSSmbParam* volatile param;
56 NCDialogParent* volatile parent;
57 };
58
59 #ifdef LIBSMBCLIENT_EXIST
SmbLogonOperCallback(void * cbData)60 int SmbLogonOperCallback( void* cbData )
61 {
62 SmbLogonCBData* data = ( SmbLogonCBData* ) cbData;
63 bool ret = GetSmbLogon( data->parent, *( data->param ) );
64 return ret ? 1 : 0;
65 }
66 #endif
67
SmbLogon(FSSmbParam * a)68 bool OF_FSCInfo::SmbLogon( FSSmbParam* a )
69 {
70 #ifdef LIBSMBCLIENT_EXIST
71 MutexLock lock( _node->GetMutex() );
72
73 if ( _node->NBStopped() ) { return false; }
74
75 OperData* p = ( OperData* ) _node->Data();
76
77 if ( !p ) { return false; }
78
79 SmbLogonCBData data;
80 data.param = a;
81 data.parent = p->Parent();
82 lock.Unlock();
83 return _node->CallBack( SmbLogonOperCallback, &data ) > 0;
84 #else
85 return false;
86 #endif
87 }
88
89
90 /////////////////////////ftp callback objs
91
92 struct FtpLogonCBData
93 {
94 FSFtpParam* volatile param;
95 NCDialogParent* volatile parent;
96 };
97
FtpLogonOperCallback(void * cbData)98 int FtpLogonOperCallback( void* cbData )
99 {
100 FtpLogonCBData* data = ( FtpLogonCBData* ) cbData;
101 bool ret = GetFtpLogon( data->parent, *( data->param ) );
102 return ret ? 1 : 0;
103 }
104
FtpLogon(FSFtpParam * a)105 bool OF_FSCInfo::FtpLogon( FSFtpParam* a )
106 {
107
108 MutexLock lock( _node->GetMutex() );
109
110 if ( _node->NBStopped() ) { return false; }
111
112 OperData* p = ( OperData* ) _node->Data();
113
114 if ( !p ) { return false; }
115
116 FtpLogonCBData data;
117 data.param = a;
118 data.parent = p->Parent();
119 lock.Unlock();
120 return _node->CallBack( FtpLogonOperCallback, &data ) > 0;
121 }
122
123 ///////////////////////////// prompt callback
124
125
PromptOperCallback(void * cbData)126 int PromptOperCallback( void* cbData )
127 {
128 PromptCBData* data = ( PromptCBData* ) cbData;
129 bool ret = GetPromptAnswer( data );
130 return ret ? 1 : 0;
131 }
132
133
Prompt(const unicode_t * header,const unicode_t * message,FSPromptData * prompts,int count)134 bool OF_FSCInfo::Prompt( const unicode_t* header, const unicode_t* message, FSPromptData* prompts, int count )
135 {
136 MutexLock lock( _node->GetMutex() );
137
138 if ( _node->NBStopped() ) { return false; }
139
140 OperData* p = ( OperData* ) _node->Data();
141
142 if ( !p ) { return false; }
143
144 PromptCBData data;
145 data.header = header;
146 data.message = message;
147 data.prompts = prompts;
148 data.count = count;
149 data.parent = p->Parent();
150 lock.Unlock();
151 return _node->CallBack( PromptOperCallback, &data ) > 0;
152 }
153
154
Stopped()155 bool OF_FSCInfo::Stopped()
156 {
157 MutexLock lock( _node->GetMutex() );
158 return _node->NBStopped();
159 };
160
~OF_FSCInfo()161 OF_FSCInfo::~OF_FSCInfo() {};
162
~OperFileThread()163 OperFileThread::~OperFileThread() {}
164
~OperRDData()165 OperRDData::~OperRDData() {}
166
167 class OperRDThread: public OperFileThread
168 {
169 clPtr<FS> fs;
170 FSPath path;
171 public:
OperRDThread(const char * opName,NCDialogParent * par,OperThreadNode * n,clPtr<FS> f,FSPath & p)172 OperRDThread( const char* opName, NCDialogParent* par, OperThreadNode* n, clPtr<FS> f, FSPath& p )
173 : OperFileThread( opName, par, n ), fs( f ), path( p ) {}
174 virtual void Run();
175 virtual ~OperRDThread();
176 };
177
Run()178 void OperRDThread::Run()
179 {
180 if ( !fs.Ptr() ) { return; }
181
182 int n = 8;
183 int ret_err;
184
185 int havePostponedStatError = 0;
186 FSString postponedStrError;
187
188 while ( true )
189 {
190 if ( !( fs->Flags() & FS::HAVE_SYMLINK ) )
191 {
192 break;
193 }
194
195 FSStat st;
196
197 // if path is inaccessible, try .. path. Throw the exception later
198 // This makes panel at least having some valid folder
199 while ( fs->Stat( path, &st, &ret_err, Info() ) )
200 {
201 havePostponedStatError = 1;
202 postponedStrError = fs->StrError( ret_err );
203
204 if ( !path.IsAbsolute() || path.Count() <= 1 || !path.Pop() )
205 {
206 throw_msg( "%s", postponedStrError.GetUtf8() );
207 }
208 }
209
210 // yell immediately if the path is inaccessible (orig behavior)
211 //if (fs->Stat(path, &st, &ret_err, Info()))
212 // throw_msg("%s", fs->StrError(ret_err).GetUtf8());
213
214 if ( !st.IsLnk() )
215 {
216 break;
217 }
218
219 n--;
220
221 if ( n < 0 )
222 {
223 throw_msg( "too many symbolic links '%s'", path.GetUtf8() );
224 }
225
226 path.Pop();
227
228 if ( !ParzeLink( path, st.link ) )
229 {
230 throw_msg( "invalid symbolic link '%s'", path.GetUtf8() );
231 }
232 }
233
234 clPtr<FSList> list = new FSList;
235
236 int havePostponedReadError = 0;
237
238 // if directory is not readable, try .. path. Throw the exception later
239 // "Stat" call above does not catch this: it checks only folder existence, but not accessibilly
240 while ( fs->ReadDir( list.ptr(), path, &ret_err, Info() ) )
241 {
242 havePostponedReadError = 1;
243 postponedStrError = fs->StrError( ret_err );
244
245 if ( !path.IsAbsolute() || path.Count() <= 1 || !path.Pop() )
246 {
247 throw_msg( "%s", postponedStrError.GetUtf8() );
248 }
249 }
250
251 // yell immediately if the dir is unreadable (orig behavior)
252 //int ret = fs->ReadDir(list.ptr(), path, &ret_err, Info());
253 //if (ret)
254 // throw_msg("%s", fs->StrError(ret_err).GetUtf8());
255
256 FSStatVfs vst;
257 fs->StatVfs( path, &vst, &ret_err, Info() );
258
259 MutexLock lock( Node().GetMutex() ); //!!!
260
261 if ( Node().NBStopped() ) { return; }
262
263 OperRDData* data = ( ( OperRDData* )Node().Data() );
264 data->list = list;
265 data->path = path;
266 data->executed = true;
267 data->vst = vst;
268
269 if ( havePostponedReadError || havePostponedStatError )
270 {
271 data->nonFatalErrorString = postponedStrError;
272 }
273 }
274
~OperRDThread()275 OperRDThread::~OperRDThread() {}
276
ReadDirThreadFunc(OperThreadNode * node)277 void ReadDirThreadFunc( OperThreadNode* node )
278 {
279 try
280 {
281 MutexLock lock( node->GetMutex() );
282
283 if ( !node->Data() ) { return; }
284
285 OperRDData* data = ( ( OperRDData* )node->Data() );
286 //dbg_printf("ReadDirThreadFunc path=%s",data->path.GetUtf8());
287 OperRDThread thread( "panel::chdir", data->Parent(), node, data->fs, data->path );
288 lock.Unlock();//!!!
289
290 try
291 {
292 thread.Run();
293 }
294 catch ( cexception* ex )
295 {
296 lock.Lock(); //!!!
297
298 if ( !node->NBStopped() ) //обязательно надо проверить, иначе 'data' может быть неактуальной
299 {
300 data->errorString = ex->message();
301 }
302
303 ex->destroy();
304 }
305 }
306 catch ( cexception* ex )
307 {
308 fprintf( stderr, "ERR!!! Error exception in ReadDirThreadFunc - '%s'\n", ex->message() );
309 ex->destroy();
310 }
311 catch ( ... )
312 {
313 fprintf( stderr, "ERR!!! Unhandled exception in ReadDirThreadFunc\n" );
314 }
315 }
316
317
318 ////////////////////////////////////////// Common file operations
319
320 enum InfoSignal
321 {
322 INFO_NEXTFILE = 2
323 };
324
325
326 class OperCFData: public OperData
327 {
328 public:
329 volatile bool executed;
330
331 clPtr<FS> srcFs; //??volatile
332 FSPath srcPath; //??volatile
333 clPtr<FSList> srcList; //??volatile
334
335 clPtr<FS> destFs; //??volatile
336 FSPath destPath; //??volatile
337
338 FSString errorString; //??volatile
339 clPtr<cstrhash<bool, unicode_t> > resList; //??volatile
340
341 Mutex infoMutex;
342 volatile bool pathChanged;
343 volatile uint64_t infoCount;
344
345 FSString infoSrcUri; //??volatile
346 FSString infoDstUri; //??volatile
347
348 volatile bool progressChanged;
349 volatile int64_t infoSize;
350 volatile int64_t infoProgress;
351 volatile unsigned infoMs; //for calculating copy speed
352 volatile int64_t infoBytes; //
353
OperCFData(NCDialogParent * p)354 OperCFData( NCDialogParent* p )
355 : OperData( p ), executed( false ),
356 pathChanged( false ), infoCount( 0 ), progressChanged( false ),
357 infoSize( 0 ), infoProgress( 0 ), infoMs( 0 ), infoBytes( 0 ) {}
358
Clear()359 void Clear()
360 {
361 executed = false;
362 errorString.Clear();
363 srcList.clear();
364
365 pathChanged = false;
366 // uint64_t infoCount = 0;
367 infoSrcUri.Clear();
368 infoDstUri.Clear();
369 progressChanged = false;
370 infoSize = 0;
371 infoProgress = 0;
372 }
373 virtual ~OperCFData();
374 };
375
~OperCFData()376 OperCFData::~OperCFData() {}
377
378 class OperCFThread: public OperFileThread
379 {
380 volatile bool commitAll;
381 volatile bool skipNonRegular;
382 enum { BSIZE = 1024 * 512, STARTSIZE = 1024 * 64 };
383 char* _buffer;
384 public:
OperCFThread(const char * opName,NCDialogParent * par,OperThreadNode * n)385 OperCFThread( const char* opName, NCDialogParent* par, OperThreadNode* n )
386 : OperFileThread( opName, par, n ),
387 commitAll( false ), skipNonRegular( false ), _buffer( 0 )
388 {
389 _buffer = new char[BSIZE];
390 }
391
392 void CreateDirectory( FS* fs, FSPath& path ); //throws
393
394 bool Unlink ( FS* fs, FSPath& path, bool* skipAll = 0 );
395 bool RmDir ( FS* fs, FSPath& path, bool* skipAll = 0 );
396 bool DeleteFile ( FS* fs, FSPath& path );
397 bool DeleteDir ( FS* fs, FSPath& path );
398 bool DeleteList ( FS* fs, FSPath& _path, FSList& list );
399
400 //from и to - эффект cptr !!!
401 bool SendCopyNextFileInfo( FSString from, FSString to );
402
403 bool SendProgressInfo( int64_t size, int64_t progress, int64_t bytes );
404
405 bool CopyLink( FS* srcFs, FSPath& srcPath, FSNode* srcNode, FS* destFs, FSPath& path, bool move );
406 // XXX CopyFile/MoveFile are #define'd in winbase.h
407 bool CopyFile( FS* srcFs, FSPath& srcPath, FSNode* srcNode, FS* destFs, FSPath& destPath, bool move );
408 bool CopyDir( FS* srcFs, FSPath& __srcPath, FSNode* srcNode, FS* destFs, FSPath& __destPath, bool move );
409 bool CopyNode( FS* srcFs, FSPath& srcPath, FSNode* srcNode, FS* destFs, FSPath& destPath, bool move );
410 bool Copy( FS* srcFs, FSPath& __srcPath, FSList* list, FS* destFs, FSPath& __destPath, cstrhash<bool, unicode_t>& resList );
411
412 int MoveFile( FS* srcFs, FSPath& srcPath, FSNode* srcNode, FS* destFs, FSPath& destPath );
413 int MoveDir( FS* srcFs, FSPath& __srcPath, FSNode* srcNode, FS* destFs, FSPath& __destPath );
414 bool MoveNode( FS* srcFs, FSPath& srcPath, FSNode* srcNode, FS* destFs, FSPath& destPath );
415 bool Move( FS* srcFs, FSPath& __srcPath, FSList* list, FS* destFs, FSPath& __destPath );
416
417 virtual ~OperCFThread();
418 };
419
~OperCFThread()420 OperCFThread::~OperCFThread()
421 {
422 if ( _buffer )
423 {
424 delete [] _buffer;
425 }
426 }
427
CreateDirectory(FS * fs,FSPath & path)428 void OperCFThread::CreateDirectory( FS* fs, FSPath& path )
429 {
430 int ret_err;
431
432 if ( fs->MkDir( path, 0777, &ret_err, Info() ) )
433 {
434 throw_msg( "%s", fs->StrError( ret_err ).GetUtf8() );
435 }
436 }
437
438 class SimpleCFThreadWin: public NCDialog
439 {
440 public:
441 OperCFData threadData;
442
SimpleCFThreadWin(NCDialogParent * parent,const char * name)443 SimpleCFThreadWin( NCDialogParent* parent, const char* name )
444 : NCDialog( ::createDialogAsChild, 0, parent, utf8_to_unicode( name ).data(), bListCancel ), threadData( parent ) {}
445 virtual void OperThreadStopped();
446 virtual ~SimpleCFThreadWin();
447 };
448
OperThreadStopped()449 void SimpleCFThreadWin::OperThreadStopped()
450 {
451 if ( !threadData.errorString.IsEmpty() )
452 {
453 NCMessageBox( ( NCDialogParent* )Parent(), _LT( "Create directory" ), threadData.errorString.GetUtf8(), true );
454 EndModal( 0 );
455 return;
456 }
457
458 EndModal( 100 );
459 }
460
~SimpleCFThreadWin()461 SimpleCFThreadWin::~SimpleCFThreadWin() {}
462
463
464 /////////////////////////////////////////////////////// MkDir
465
MkDirThreadFunc(OperThreadNode * node)466 void MkDirThreadFunc( OperThreadNode* node )
467 {
468 try
469 {
470 MutexLock lock( node->GetMutex() );
471
472 if ( !node->Data() ) { return; }
473
474 OperCFData* data = ( ( OperCFData* )node->Data() );
475 OperCFThread thread( "create directory", data->Parent(), node );
476
477 clPtr<FS> fs = data->srcFs;
478 FSPath path = data->srcPath;
479
480 lock.Unlock();//!!!
481
482 try
483 {
484 thread.CreateDirectory( fs.Ptr(), path );
485 }
486 catch ( cexception* ex )
487 {
488 lock.Lock(); //!!!
489
490 if ( !node->NBStopped() ) //обязательно надо проверить, иначе 'data' может быть неактуальной
491 {
492 data->errorString = ex->message();
493 }
494
495 ex->destroy();
496 }
497 }
498 catch ( cexception* ex )
499 {
500 fprintf( stderr, "ERR!!! Error exception in MkDirThreadFunc - '%s'\n", ex->message() );
501 ex->destroy();
502 }
503 catch ( ... )
504 {
505 fprintf( stderr, "ERR!!! Unhandled exception in MkDirThreadFunc\n" );
506 }
507 }
508
MkDir(clPtr<FS> f,FSPath & p,NCDialogParent * parent)509 bool MkDir( clPtr<FS> f, FSPath& p, NCDialogParent* parent )
510 {
511 SimpleCFThreadWin dlg( parent, _LT( "Create directory" ) );
512 dlg.threadData.Clear();
513 dlg.threadData.srcFs = f;
514 dlg.threadData.srcPath = p;
515 dlg.RunNewThread( "Create directory", MkDirThreadFunc, &dlg.threadData ); //может быть исключение
516 dlg.Enable();
517 dlg.Show();
518 dlg.DoModal();
519 dlg.StopThread();
520 return dlg.threadData.errorString.IsEmpty();
521 }
522
523
524 ///////////////////////////////////////////////////// DELETE
525
526 //возвращает true если можно продолжать процесс
527
Unlink(FS * fs,FSPath & path,bool * skipAll)528 bool OperCFThread::Unlink( FS* fs, FSPath& path, bool* skipAll )
529 {
530 int ret_err;
531
532 while ( fs->Delete( path, &ret_err, Info() ) )
533 {
534 if ( skipAll && *skipAll ) { return true; }
535
536 switch ( RedMessage( _LT( "Can`t delete file:\n" ), fs->Uri( path ).GetUtf8(), skipAll ? bRetrySkipSkipallCancel : bRetrySkipCancel,
537 fs->StrError( ret_err ).GetUtf8() ) )
538 {
539 case CMD_SKIPALL:
540 if ( skipAll ) { *skipAll = true; } //no break
541
542 case CMD_SKIP:
543 return true;
544
545 case CMD_RETRY:
546 continue;
547
548 default:
549 return false;
550 }
551 }
552
553 return true;
554 }
555
RmDir(FS * fs,FSPath & path,bool * skipAll)556 bool OperCFThread::RmDir( FS* fs, FSPath& path, bool* skipAll )
557 {
558 int ret_err;
559
560 while ( fs->RmDir( path, &ret_err, Info() ) )
561 {
562 if ( skipAll && *skipAll ) { return true; }
563
564 switch ( RedMessage( _LT( "Can`t delete directory:\n" ) , fs->Uri( path ).GetUtf8(), skipAll ? bRetrySkipSkipallCancel : bRetrySkipCancel,
565 fs->StrError( ret_err ).GetUtf8() ) )
566 {
567 case CMD_SKIPALL:
568 if ( skipAll ) { *skipAll = true; } //no break
569
570 case CMD_SKIP:
571 return true;
572
573 case CMD_RETRY:
574 continue;
575
576 default:
577 return false;
578 }
579 }
580
581 return true;
582 }
583
DeleteFile(FS * fs,FSPath & path)584 bool OperCFThread::DeleteFile( FS* fs, FSPath& path ) //return true if not concelled
585 {
586 if ( Info()->Stopped() ) { return false; }
587
588 if ( !commitAll )
589 {
590 switch ( RedMessage( _LT( "Do you want to delete file?\n" ), fs->Uri( path ).GetUtf8(), bDeleteAllSkipCancel ) )
591 {
592 case CMD_SKIP:
593 return true;
594
595 case CMD_ALL:
596 commitAll = true;
597 break;
598
599 case CMD_OK:
600 break;
601
602 default:
603 return false;
604 }
605 }
606
607 return Unlink( fs, path ); //skip all???
608 }
609
DeleteDir(FS * fs,FSPath & path)610 bool OperCFThread::DeleteDir( FS* fs, FSPath& path )
611 {
612 if ( Info()->Stopped() ) { return false; }
613
614 FSList list;
615
616 while ( true )
617 {
618 int ret_err;
619 int ret = fs->ReadDir( &list, path, &ret_err, Info() );
620
621 if ( ret == -2 ) { return false; }
622
623 if ( !ret ) { break; }
624
625 switch ( RedMessage( _LT( "Can`t open directory:\n" ), fs->Uri( path ).GetUtf8(), bRetrySkipCancel, fs->StrError( ret_err ).GetUtf8() ) )
626 {
627 case CMD_SKIP:
628 return true;
629
630 case CMD_RETRY:
631 continue;
632
633 default:
634 return false;
635 }
636 }
637
638 return DeleteList( fs, path, list );
639 }
640
DeleteList(FS * fs,FSPath & _path,FSList & list)641 bool OperCFThread::DeleteList( FS* fs, FSPath& _path, FSList& list )
642 {
643 if ( Info()->Stopped() ) { return false; }
644
645 FSPath path = _path;
646 int cnt = path.Count();
647
648 for ( FSNode* node = list.First(); node; node = node->next )
649 {
650 if ( node->extType ) { continue; }
651
652 path.SetItemStr( cnt, node->Name() );
653
654 if ( node->IsDir() && !node->st.IsLnk() )
655 {
656 if ( !DeleteDir( fs, path ) ) { return false; }
657
658 if ( !RmDir( fs, path ) ) { return false; }
659
660 continue;
661 }
662
663 if ( !DeleteFile( fs, path ) ) { return false; }
664 }
665
666 return true;
667 }
668
669
DeleteThreadFunc(OperThreadNode * node)670 void DeleteThreadFunc( OperThreadNode* node )
671 {
672 try
673 {
674 MutexLock lock( node->GetMutex() );
675
676 if ( !node->Data() ) { return; }
677
678 OperCFData* data = ( ( OperCFData* )node->Data() );
679 OperCFThread thread( "Delete", data->Parent(), node );
680
681 clPtr<FS> fs = data->srcFs;
682 FSPath path = data->srcPath;
683 clPtr<FSList> list = data->srcList;
684
685 lock.Unlock();//!!!
686
687 try
688 {
689 if ( list.ptr() )
690 {
691 thread.DeleteList( fs.Ptr(), path, *( list.ptr() ) );
692 }
693 }
694 catch ( cexception* ex )
695 {
696 lock.Lock(); //!!!
697
698 if ( !node->NBStopped() ) //обязательно надо проверить, иначе 'data' может быть неактуальной
699 {
700 data->errorString = ex->message();
701 }
702
703 ex->destroy();
704 }
705 }
706 catch ( cexception* ex )
707 {
708 fprintf( stderr, "ERR!!! Error exception in DeleteThreadFunc - '%s'\n", ex->message() );
709 ex->destroy();
710 }
711 catch ( ... )
712 {
713 fprintf( stderr, "ERR!!! Unhandled exception in DeleteThreadFunc\n" );
714 }
715 }
716
DeleteList(clPtr<FS> f,FSPath & p,clPtr<FSList> list,NCDialogParent * parent)717 bool DeleteList( clPtr<FS> f, FSPath& p, clPtr<FSList> list, NCDialogParent* parent )
718 {
719 SimpleCFThreadWin dlg( parent, _LT( "Delete" ) );
720 dlg.threadData.Clear();
721 dlg.threadData.srcFs = f;
722 dlg.threadData.srcPath = p;
723 dlg.threadData.srcList = list;
724 dlg.RunNewThread( "Delete", DeleteThreadFunc, &dlg.threadData ); //может быть исключение
725 dlg.Enable();
726 dlg.Show();
727 dlg.DoModal();
728 dlg.StopThread();
729 return dlg.threadData.errorString.IsEmpty();
730 }
731
732 /////////////////////////////////////////////////////////////// Copy
733
SendCopyNextFileInfo(FSString from,FSString to)734 bool OperCFThread::SendCopyNextFileInfo( FSString from, FSString to )
735 {
736 MutexLock lock( Node().GetMutex() );
737
738 if ( Node().NBStopped() ) { return false; }
739
740 OperCFData* data = ( ( OperCFData* )Node().Data() );
741
742 if ( !data ) { return false; }
743
744 data->infoCount++;
745 data->infoSrcUri = from;
746 data->infoDstUri = to;
747 data->pathChanged = true;
748
749 if ( !WinThreadSignal( INFO_NEXTFILE ) ) { return false; }
750
751 data->infoProgress = 0;
752
753 return true;
754 }
755
756 namespace wal
757 {
758 extern unsigned GetTickMiliseconds();
759 };
760
SendProgressInfo(int64_t size,int64_t progress,int64_t bytes)761 bool OperCFThread::SendProgressInfo( int64_t size, int64_t progress, int64_t bytes )
762 {
763 MutexLock lock( Node().GetMutex() );
764
765 if ( Node().NBStopped() ) { return false; }
766
767 OperCFData* data = ( ( OperCFData* )Node().Data() );
768
769 if ( !data ) { return false; }
770
771 data->infoSize = size;
772 data->infoProgress = progress;
773 data->infoBytes += bytes;
774 data->infoMs = GetTickMiliseconds();
775 data->progressChanged = true;
776
777 if ( !WinThreadSignal( INFO_NEXTFILE ) ) { return false; }
778
779 return true;
780 }
781
OperFileNameWin(Win * parent,int ccount)782 OperFileNameWin::OperFileNameWin( Win* parent, int ccount )
783 : Win( Win::WT_CHILD, 0, parent, 0 )
784 , _ccount( ccount )
785 {
786 wal::GC gc( this );
787 cpoint size = GetStaticTextExtent( gc, ABCString, GetFont() );
788 size.x = size.x / ABCStringLen * ccount;
789
790 if ( size.x > 500 ) { size.x = 550; }
791
792 SetLSize( LSize( size ) );
793 }
794
SetText(const unicode_t * s)795 void OperFileNameWin::SetText( const unicode_t* s )
796 {
797 text = new_unicode_str( s );
798 Invalidate();
799 }
800
Paint(wal::GC & gc,const crect & paintRect)801 void OperFileNameWin::Paint( wal::GC& gc, const crect& paintRect )
802 {
803 crect rect = ClientRect();
804 gc.SetFillColor( UiGetColor( uiBackground, 0, 0, 0xFFFFFF ) );
805 gc.FillRect( rect );
806 gc.SetTextColor( UiGetColor( uiColor, 0, 0, 0 ) );
807 gc.Set( GetFont() );
808
809 unicode_t* p = text.data();
810
811 if ( p )
812 {
813 int l = unicode_strlen( p );
814
815 if ( l > _ccount )
816 {
817 p += l - _ccount;
818 }
819
820 DrawStaticText( gc, 0, 0, p );
821 }
822 }
823
~OperFileNameWin()824 OperFileNameWin::~OperFileNameWin() {};
825
826
827 class NCNumberWin: public Win
828 {
829 int64_t _num;
830 public:
NCNumberWin(Win * parent,int width=10)831 NCNumberWin( Win* parent, int width = 10 )
832 : Win( Win::WT_CHILD, 0, parent, 0 ), _num( 0 )
833 {
834 wal::GC gc( this );
835 cpoint size = GetStaticTextExtent( gc, ABCString, GetFont() );
836 size.x = size.x / ABCStringLen * width;
837 SetLSize( LSize( size ) );
838 }
839
SetNumber(int64_t n)840 void SetNumber( int64_t n )
841 {
842 if ( _num == n ) { return; }
843
844 _num = n;
845 Invalidate();
846 }
847
848 void Paint( wal::GC& gc, const crect& paintRect );
849 virtual ~NCNumberWin();
850 };
851
Paint(wal::GC & gc,const crect & paintRect)852 void NCNumberWin::Paint( wal::GC& gc, const crect& paintRect )
853 {
854 crect rect = ClientRect();
855 gc.SetFillColor( UiGetColor( uiBackground, 0, 0, 0xFFFFFF ) );
856 gc.FillRect( rect );
857 gc.SetTextColor( UiGetColor( uiColor, 0, 0, 0 ) );
858 gc.Set( GetFont() );
859
860 unicode_t c[32];
861 unicode_t buf[32];
862 unicode_t* s = buf;
863
864 int64_t n = _num;
865 bool minus = n < 0;
866
867 if ( minus ) { n = -n; }
868
869 int i = 0;
870
871 for ( ; n > 0; i++, n /= 10 ) { c[i] = char( n % 10 ) + '0'; }
872
873 if ( minus ) { *s = '-'; s++; }
874
875 if ( i == 0 ) {*s = '0'; s++; };
876
877 for ( i--; i >= 0; i-- ) { *( s++ ) = c[i]; }
878
879 *s = 0;
880
881 gc.TextOut( 0, 0, buf );
882 }
883
~NCNumberWin()884 NCNumberWin::~NCNumberWin() {}
885
886
887 class NCProgressWin: public Win
888 {
889 int64_t _from, _to;
890 int64_t _num;
891 int64_t _lastWidth, _lastPos;
892 public:
NCProgressWin(Win * parent)893 NCProgressWin( Win* parent )
894 : Win( Win::WT_CHILD, 0, parent, 0 ), _from( 0 ), _to( 0 ), _num( 0 ), _lastWidth( 0 ), _lastPos( 0 )
895
896 {
897 cpoint size;
898 size.x = 10;
899 size.y = 16;
900 LSize ls( size );
901 ls.x.maximal = 1000;
902 SetLSize( ls );
903 }
904
SetData(int64_t from,int64_t to,int64_t num)905 void SetData( int64_t from, int64_t to, int64_t num )
906 {
907 if ( _from == from && _to == to && _num == num ) { return; }
908
909 bool diapazonChanged = _from != from || _to != to;
910
911 _from = from;
912 _to = to;
913 _num = num;
914
915 if ( _num < _from || _to <= _from ) { return; }
916
917 int64_t size = _to - _from;
918 int64_t n = ( _lastWidth * _num ) / size;
919
920 if ( diapazonChanged || _lastPos != n )
921 {
922 Invalidate();
923 }
924 }
925
926 virtual void Paint( wal::GC& gc, const crect& paintRect );
927 virtual ~NCProgressWin();
928 };
929
930
MidAB(int a,int b,int i,int n)931 inline unsigned MidAB( int a, int b, int i, int n )
932 {
933 return n > 1 ? a + ( ( b - a ) * i ) / ( n - 1 ) : a;
934 }
935
FillHorisont(wal::GC & gc,crect rect,unsigned a,unsigned b)936 static void FillHorisont( wal::GC& gc, crect rect, unsigned a, unsigned b )
937 {
938 if ( rect.IsEmpty() ) { return; }
939
940 unsigned ar = a & 0xFF, ag = ( a >> 8 ) & 0xFF, ab = ( a >> 16 ) & 0xFF,
941 br = b & 0xFF, bg = ( b >> 8 ) & 0xFF, bb = ( b >> 16 ) & 0xFF;
942
943 int h = rect.Height();
944 int x1 = rect.left, x2 = rect.right;
945
946 if ( h <= 0 || x1 >= x2 ) { return; }
947
948 for ( int i = 0; i < h; i++ )
949 {
950 unsigned color = ( MidAB( ar, br, i, h ) & 0xFF ) + ( ( MidAB( ag, bg, i, h ) & 0xFF ) << 8 ) + ( ( MidAB( ab, bb, i, h ) & 0xFF ) << 16 );
951 gc.SetLine( color );
952 gc.MoveTo( x1, rect.top + i );
953 gc.LineTo( x2, rect.top + i );
954 }
955
956 }
957
Paint(wal::GC & gc,const crect & paintRect)958 void NCProgressWin::Paint( wal::GC& gc, const crect& paintRect )
959 {
960 crect rect = ClientRect();
961 int w = rect.Width();
962
963 Draw3DButtonW2( gc, rect, 0x808080, false );
964 rect.Dec();
965 rect.Dec();
966 w -= 2;
967
968 if ( !( _num < _from || _to <= _from || w <= 0 ) )
969 {
970 int64_t size = _to - _from;
971 int n = int( ( w * _num ) / size );
972
973 crect r = rect;
974 r.right = n;
975
976 unsigned color = 0xA00000;
977 unsigned bColor = ColorTone( color, -80 ), aColor = ColorTone( color, +80 );
978 FillHorisont( gc, r, aColor, bColor );
979
980 _lastWidth = w;
981 _lastPos = n;
982 rect.left += n;
983 }
984
985 unsigned color = 0xB0B0B0;
986 unsigned bColor = ColorTone( color, +50 ), aColor = ColorTone( color, -50 );
987 FillHorisont( gc, rect, aColor, bColor );
988
989 }
990
~NCProgressWin()991 NCProgressWin::~NCProgressWin() {}
992
993
994 class CopyDialog: public SimpleCFThreadWin
995 {
996 Layout _layout;
997 StaticLine _text1;
998 StaticLine _text2;
999 StaticLine _text3;
1000 OperFileNameWin _from, _to;
1001 NCNumberWin _countWin;
1002 NCProgressWin _progressWin;
1003 StaticLine _speedStr;
1004 enum
1005 {
1006 SPEED_NODE_COUNT = 10
1007 };
1008 struct SpeedNode
1009 {
1010 unsigned deltaMs;
1011 int64_t bytes;
SpeedNodeCopyDialog::SpeedNode1012 SpeedNode(): deltaMs( 0 ), bytes( 0 ) {}
1013 } _speedList[SPEED_NODE_COUNT];
1014
1015 unsigned _lastMs;
1016
1017 public:
CopyDialog(NCDialogParent * parent,bool move=false)1018 CopyDialog( NCDialogParent* parent, bool move = false )
1019 : SimpleCFThreadWin( parent, move ? _LT( "Move" ) : _LT( "Copy" ) ),
1020 _layout( 7, 2 ),
1021 _text1( 0, this, utf8_to_unicode( move ? _LT( "Moving the file" ) : _LT( "Copying the file" ) ).data() ),
1022 _text2( 0, this, utf8_to_unicode( _LT( "to" ) ).data() ),
1023 _text3( 0, this, utf8_to_unicode( _LT( "Files processed" ) ).data() ),
1024 _from( this ),
1025 _to( this ),
1026 _countWin( this ),
1027 _progressWin( this ),
1028 _speedStr( uiValue, this, 0, 0, StaticLine::LEFT, 10 ),
1029 _lastMs( GetTickMiliseconds() )
1030 {
1031 _layout.AddWin( &_text1, 0, 0, 0, 1 );
1032 _layout.AddWin( &_from, 1, 0, 1, 1 );
1033 _layout.AddWin( &_text2, 2, 0, 2, 1 );
1034 _layout.AddWin( &_to, 3, 0, 3, 1 );
1035 _layout.AddWin( &_progressWin, 4, 0, 4, 1 );
1036 _layout.AddWin( &_text3, 5, 0 );
1037 _layout.AddWin( &_countWin, 5, 1 );
1038 _layout.AddWin( &_speedStr, 6, 0 );
1039 _text1.Show();
1040 _text1.Enable();
1041 _text2.Show();
1042 _text2.Enable();
1043 _text3.Show();
1044 _text3.Enable();
1045 _from.Show();
1046 _from.Enable();
1047 _to.Show();
1048 _to.Enable();
1049 _countWin.Show();
1050 _countWin.Enable();
1051 _progressWin.Show();
1052 _progressWin.Enable();
1053 _speedStr.Show();
1054 _speedStr.Enable();
1055 AddLayout( &_layout );
1056 SetTimer( 1, 1000 );
1057 SetPosition();
1058 }
1059
1060 virtual void OperThreadSignal( int info );
1061 virtual void EventTimer( int tid );
1062 virtual ~CopyDialog();
1063 };
1064
1065 #define GIG (int64_t(1024)*1024*1024)
1066 #define MEG (int64_t(1024)*1024)
1067 #define KIL (int64_t(1024))
1068
GetSmallPrintableSpeedStr(char buf[64],int64_t size)1069 static char* GetSmallPrintableSpeedStr( char buf[64], int64_t size )
1070 {
1071 char str[16];
1072 str[0] = 0;
1073
1074 int64_t num = size;
1075 int mod = 0;
1076
1077 if ( num >= GIG ) //:)
1078 {
1079 mod = ( num % GIG ) / ( GIG / 10 );
1080 num /= GIG;
1081 str[0] = ' ';
1082 str[1] = 'G';
1083 str[2] = 'b';
1084 str[3] = '/';
1085 str[4] = 's';
1086 str[5] = 0;
1087 }
1088 else if ( num >= MEG )
1089 {
1090 mod = ( num % MEG ) / ( MEG / 10 );
1091 num /= MEG;
1092 str[0] = ' ';
1093 str[1] = 'M';
1094 str[2] = 'b';
1095 str[3] = '/';
1096 str[4] = 's';
1097 str[5] = 0;
1098 }
1099 else if ( num >= KIL )
1100 {
1101 mod = ( num % KIL ) / ( KIL / 10 );
1102 num /= KIL;
1103 str[0] = ' ';
1104 str[1] = 'K';
1105 str[2] = 'b';
1106 str[3] = '/';
1107 str[4] = 's';
1108 str[5] = 0;
1109 }
1110 else { mod = -1; }
1111
1112
1113 char dig[64];
1114 char* t = unsigned_to_char<int64_t>( num, dig );
1115
1116 if ( mod >= 0 ) { t--; t[0] = '.'; t[1] = mod + '0'; t[2] = 0; }
1117
1118 char* us = buf;
1119
1120 for ( char* s = dig; *s; s++ )
1121 {
1122 *( us++ ) = *s;
1123 }
1124
1125 for ( char* t = str; *t; t++ )
1126 {
1127 *( us++ ) = *t;
1128 }
1129
1130 *us = 0;
1131
1132 return buf;
1133 }
1134
EventTimer(int tid)1135 void CopyDialog::EventTimer( int tid )
1136 {
1137 if ( tid == 1 )
1138 {
1139 unsigned ms = 0;
1140 int64_t bytes = 0;
1141
1142 {
1143 MutexLock lock( &threadData.infoMutex );
1144 ms = threadData.infoMs;
1145 bytes = threadData.infoBytes;
1146 threadData.infoBytes = 0;
1147 lock.Unlock();
1148 }
1149
1150 //shift
1151 int i;
1152
1153 for ( i = SPEED_NODE_COUNT - 1; i > 0; i-- )
1154 {
1155 _speedList[i] = _speedList[i - 1];
1156 }
1157
1158 _speedList[0].bytes = bytes;
1159
1160 if ( bytes > 0 )
1161 {
1162 unsigned n = ms - _lastMs;
1163 _speedList[0].deltaMs = n > 1000000 ? 0 : n;
1164 _lastMs = ms;
1165 }
1166 else
1167 {
1168 _speedList[0].deltaMs = 0;
1169 }
1170
1171 int64_t sumBytes = 0;
1172 int64_t sumMs = 0;
1173
1174 for ( i = 0; i < SPEED_NODE_COUNT; i++ )
1175 {
1176 sumMs += _speedList[i].deltaMs;
1177 sumBytes += _speedList[i].bytes;
1178 }
1179
1180 int64_t speed = 0;
1181
1182 if ( sumMs > 0 && sumBytes > 0 )
1183 {
1184 speed = ( sumBytes * 1000 ) / sumMs;
1185 }
1186
1187 char buf[64];
1188 _speedStr.SetText( utf8_to_unicode( GetSmallPrintableSpeedStr( buf, speed ) ).data() );
1189
1190 return;
1191 }
1192 }
1193
OperThreadSignal(int info)1194 void CopyDialog::OperThreadSignal( int info )
1195 {
1196 if ( info == INFO_NEXTFILE )
1197 {
1198 MutexLock lock( &threadData.infoMutex );
1199
1200 if ( threadData.pathChanged )
1201 {
1202 _from.SetText( threadData.infoSrcUri.GetUnicode() );
1203 _to.SetText( threadData.infoDstUri.GetUnicode() );
1204 _countWin.SetNumber( threadData.infoCount );
1205 threadData.pathChanged = false;
1206 }
1207
1208 if ( threadData.progressChanged )
1209 {
1210 _progressWin.SetData( 0, threadData.infoSize, threadData.infoProgress );
1211 threadData.progressChanged = false;
1212 }
1213 }
1214 }
1215
~CopyDialog()1216 CopyDialog::~CopyDialog() {}
1217
IsSameFile(FS * srcFs,FSPath & srcPath,FSStat * srcStat,FS * destFs,FSPath & destPath)1218 static bool IsSameFile( FS* srcFs, FSPath& srcPath, FSStat* srcStat, FS* destFs, FSPath& destPath )
1219 {
1220 if ( srcFs->Type() != destFs->Type() ) { return false; }
1221
1222 #ifndef _WIN32
1223
1224 if ( destFs->Type() == FS::SYSTEM )
1225 {
1226 FSStat st;
1227 return ( !destFs->Stat( destPath, &st, 0, 0 ) && srcStat->dev == st.dev && srcStat->ino == st.ino );
1228 }
1229
1230 #endif
1231
1232 FSString s = destFs->Uri( destPath );
1233 return !srcFs->Uri( srcPath ).Cmp( s );
1234 }
1235
CopyLink(FS * srcFs,FSPath & srcPath,FSNode * srcNode,FS * destFs,FSPath & destPath,bool move)1236 bool OperCFThread::CopyLink( FS* srcFs, FSPath& srcPath, FSNode* srcNode, FS* destFs, FSPath& destPath, bool move )
1237 {
1238 ASSERT( srcNode->st.IsLnk() );
1239
1240 if ( IsSameFile( srcFs, srcPath, &( srcNode->st ), destFs, destPath ) )
1241 {
1242 RedMessage( _LT( "Can't copy link to itself:\n" ) , srcFs->Uri( srcPath ).GetUtf8() );
1243 return false;
1244 }
1245
1246 int ret_err;
1247
1248 while ( destFs->Symlink( destPath, srcNode->st.link, &ret_err, Info() ) && !skipNonRegular )
1249 switch ( RedMessage( _LT( "Can't create symbolic link:\n" ), destFs->Uri( destPath ).GetUtf8(), "to\n",
1250 srcNode->st.link.GetUtf8(), bSkipSkipallCancel, destFs->StrError( ret_err ).GetUtf8() ) )
1251 {
1252 case CMD_CANCEL:
1253 return false;
1254
1255 case CMD_SKIPALL:
1256 skipNonRegular = true;
1257
1258 case CMD_SKIP:
1259 return true;
1260 }
1261
1262 return !move || Unlink( srcFs, srcPath );
1263 }
1264
1265
1266 //#define BUFSIZE (1024*512) //(1024*64)
1267
1268 //inline FSString Err(FS *fs, int err){ return fs->StrError(err); }
1269
CopyFile(FS * srcFs,FSPath & srcPath,FSNode * srcNode,FS * destFs,FSPath & destPath,bool move)1270 bool OperCFThread::CopyFile( FS* srcFs, FSPath& srcPath, FSNode* srcNode, FS* destFs, FSPath& destPath, bool move )
1271 {
1272 if ( !srcNode->st.IsReg() && !skipNonRegular )
1273 switch ( RedMessage( _LT( "Can't copy the links or special file:\n" ), srcFs->Uri( srcPath ).GetUtf8(), bSkipSkipallCancel ) )
1274 {
1275 case CMD_SKIPALL:
1276 skipNonRegular = true; // no break
1277
1278 case CMD_SKIP:
1279 return true;
1280
1281 default:
1282 return false;
1283 }
1284
1285 if ( IsSameFile( srcFs, srcPath, &( srcNode->st ), destFs, destPath ) )
1286 {
1287 RedMessage( _LT( "Can't copy file to itself:\n" ) , srcFs->Uri( srcPath ).GetUtf8() );
1288 return false;
1289 }
1290
1291 SendCopyNextFileInfo( srcFs->Uri( srcPath ), destFs->Uri( destPath ) );
1292 SendProgressInfo( srcNode->st.size, 0, 0 );
1293
1294 bool stopped = false;
1295
1296 int ret_err;
1297
1298 int in = -1;
1299
1300 if (destFs->Type() == FS::TYPES::TMP)
1301 {
1302 // copy and move to tmpfs work the same way. Should we yell on move op instead?
1303 FSTmp* destTmpFS = static_cast<FSTmp*>(destFs);
1304 if (!destTmpFS->BaseIsEqual(srcFs))
1305 {
1306 RedMessage(_LT("Temporary panel can store only files from the same file system:\n"));
1307 return false;
1308 }
1309 if (srcFs->Type() == FS::TMP && srcNode->IsReg())
1310 {
1311 FSString srcFullFSString(srcNode->name.GetUnicode());
1312 FSPath srcFullFSPath(srcFullFSString);
1313 //srcFullFSPath.dbg_printf("OperCFThread::CopyFile srcFullFSPath=");
1314 //destPath.dbg_printf("OperCFThread::CopyFile destPath=");
1315 FSPath destDir(destPath);
1316 destDir.Pop();
1317 return destTmpFS->AddNode(srcFullFSPath, destDir);
1318 }
1319 else
1320 return destTmpFS->AddNode(srcPath, srcNode, destPath);
1321 }
1322
1323 while ( true )
1324 {
1325 in = srcFs->OpenRead( srcPath, FS::SHARE_READ, &ret_err, Info() );
1326
1327 if ( in == -2 ) { return false; }
1328
1329 if ( in >= 0 ) { break; }
1330
1331 switch ( RedMessage( _LT( "Can't open file:\n" ) , srcFs->Uri( srcPath ).GetUtf8(), bRetrySkipCancel, srcFs->StrError( ret_err ).GetUtf8() ) )
1332 {
1333 case CMD_CANCEL:
1334 return false;
1335
1336 case CMD_SKIP:
1337 return true;
1338 }
1339 }
1340
1341 int out = -1;
1342
1343 out = destFs->OpenCreate( destPath, false | commitAll, srcNode->st.mode, 0, &ret_err, Info() );
1344
1345 if ( out < 0 && destFs->IsEEXIST( ret_err ) )
1346 switch ( RedMessage( _LT( "Overwrite file?\n" ) , destFs->Uri( destPath ).GetUtf8(), bOkAllNoCancel ) )
1347 {
1348 case CMD_ALL:
1349 commitAll = true; //no break
1350
1351 case CMD_OK:
1352 out = destFs->OpenCreate( destPath, true, srcNode->st.mode, 0, &ret_err, Info() );
1353 break;
1354
1355 case CMD_NO:
1356 srcFs->Close( in, 0, Info() );
1357 return true;
1358
1359 default:
1360 srcFs->Close( in, 0, Info() );
1361 return false;
1362 }
1363
1364 if ( out < 0 )
1365 {
1366 srcFs->Close( in, 0, Info() );
1367 return RedMessage( _LT( "Can't create file:\n" ), destFs->Uri( destPath ).GetUtf8(), bSkipCancel, destFs->StrError( ret_err ).GetUtf8() ) == CMD_SKIP;
1368 }
1369
1370 int bytes;
1371 //char buf[BUFSIZE];
1372 int64_t doneBytes = 0;
1373
1374 int blockSize = STARTSIZE;
1375
1376 while ( true )
1377 {
1378 if ( Info()->Stopped() )
1379 {
1380 stopped = true;
1381 goto err;
1382 }
1383
1384 time_t timeStart = time( 0 );
1385
1386 if ( ( bytes = srcFs->Read( in, _buffer, blockSize, &ret_err, Info() ) ) < 0 )
1387 {
1388 if ( bytes == -2 ||
1389 RedMessage( _LT( "Can't read the file:\n" ), srcFs->Uri( srcPath ).GetUtf8(), bSkipCancel, srcFs->StrError( ret_err ).GetUtf8() ) != CMD_SKIP )
1390 {
1391 stopped = true;
1392 }
1393
1394 goto err;
1395 }
1396
1397 if ( !bytes ) { break; }
1398
1399 int b;
1400
1401 if ( ( b = destFs->Write( out, _buffer, bytes, &ret_err, Info() ) ) < 0 )
1402 {
1403 if ( b == -2 || RedMessage( _LT( "Can't write the file:\n" ), destFs->Uri( destPath ).GetUtf8(), bSkipCancel, destFs->StrError( ret_err ).GetUtf8() ) != CMD_SKIP )
1404 {
1405 stopped = true;
1406 }
1407
1408 goto err;
1409 }
1410
1411
1412 if ( b != bytes )
1413 {
1414 if ( RedMessage( "May be disk full \n(writed bytes != readed bytes)\nwhen write:\n", destFs->Uri( destPath ).GetUtf8(), bSkipCancel ) != CMD_SKIP )
1415 {
1416 stopped = true;
1417 }
1418
1419 goto err;
1420 }
1421
1422 time_t timeStop = time( 0 );
1423
1424 if ( timeStart == timeStop && blockSize < BSIZE )
1425 {
1426 blockSize = blockSize * 2;
1427
1428 if ( blockSize > BSIZE ) { blockSize = BSIZE; }
1429 }
1430
1431 doneBytes += bytes;
1432 SendProgressInfo( srcNode->st.size, doneBytes, bytes );
1433 }
1434
1435 srcFs->Close( in, 0, Info() );
1436 in = -1;
1437
1438 SendProgressInfo( srcNode->st.size, srcNode->st.size, 0 );
1439
1440 {
1441 int r = destFs->Close( out, &ret_err, Info() );
1442
1443 if ( r )
1444 {
1445 if ( r == -2 || RedMessage( "Can't close the file:\n", destFs->Uri( destPath ).GetUtf8(), bSkipCancel, destFs->StrError( ret_err ).GetUtf8() ) != CMD_SKIP )
1446 {
1447 stopped = true;
1448 }
1449
1450 goto err;
1451 }
1452 else
1453 {
1454 out = -1;
1455 }
1456 }
1457
1458 destFs->SetFileTime( destPath, srcNode->st.m_CreationTime, srcNode->st.m_LastWriteTime, srcNode->st.m_LastWriteTime, 0, Info() );
1459
1460 return !move || Unlink( srcFs, srcPath );
1461
1462 err:
1463
1464 if ( in >= 0 ) { srcFs->Close( in, 0, Info() ); }
1465
1466 if ( out >= 0 ) { destFs->Close( out, 0, Info() ); }
1467
1468 Unlink( destFs, destPath );
1469
1470 return !stopped;
1471 }
1472
CopyDir(FS * srcFs,FSPath & __srcPath,FSNode * srcNode,FS * destFs,FSPath & __destPath,bool move)1473 bool OperCFThread::CopyDir( FS* srcFs, FSPath& __srcPath, FSNode* srcNode, FS* destFs, FSPath& __destPath, bool move )
1474 {
1475 if ( Info()->Stopped() ) { return false; }
1476
1477 FSList list;
1478
1479 int ret_error;
1480
1481 while ( true )
1482 {
1483 int ret = srcFs->ReadDir( &list, __srcPath, &ret_error, Info() );
1484
1485 if ( ret == -2 ) { return false; }
1486
1487 if ( !ret ) { break; }
1488
1489 switch ( RedMessage( _LT( "Can`t open directory:\n" ) , srcFs->Uri( __srcPath ).GetUtf8(), bRetrySkipCancel, srcFs->StrError( ret_error ).GetUtf8() ) )
1490 {
1491 case CMD_SKIP:
1492 return true;
1493
1494 case CMD_RETRY:
1495 continue;
1496
1497 default:
1498 return false;
1499 }
1500 }
1501
1502 while ( destFs->MkDir( __destPath, MkDirMode, &ret_error, Info() ) && !destFs->IsEEXIST( ret_error ) )
1503 {
1504 switch ( RedMessage( _LT( "Can't create the directory:\n" ), destFs->Uri( __destPath ).GetUtf8(), bRetrySkipCancel, destFs->StrError( ret_error ).GetUtf8() ) )
1505 {
1506 case CMD_CANCEL:
1507 return false;
1508
1509 case CMD_SKIP:
1510 return true;
1511 }
1512 }
1513
1514
1515 FSPath srcPath = __srcPath;
1516 int srcPos = srcPath.Count();
1517 FSPath destPath = __destPath;
1518 int destPos = destPath.Count();
1519
1520
1521 for ( FSNode* node = list.First(); node; node = node->next )
1522 {
1523 if ( Info()->Stopped() ) { return false; }
1524
1525 srcPath.SetItemStr( srcPos, node->Name() );
1526 destPath.SetItemStr( destPos, node->Name() );
1527
1528 if ( !CopyNode( srcFs, srcPath, node, destFs, destPath, move ) ) { return false; }
1529 }
1530
1531 destFs->SetFileTime( destPath, srcNode->st.m_CreationTime, srcNode->st.m_LastWriteTime, srcNode->st.m_LastWriteTime, 0, Info() );
1532
1533 return !move || RmDir( srcFs, __srcPath );
1534 }
1535
stripPathFromLastItem(FSPath & path)1536 static void stripPathFromLastItem(FSPath& path)
1537 {
1538 FSString* lastItem = path.GetItem(path.Count() - 1);
1539 if (lastItem)
1540 {
1541 const unicode_t* lastU = lastItem->GetUnicode();
1542 const unicode_t* lastDelim = unicode_strrchr(lastU, DIR_SPLITTER);
1543 if (lastDelim != 0)
1544 {
1545 path.SetItemStr(path.Count() - 1,FSString(lastDelim + 1));
1546 }
1547 }
1548 }
1549
CopyNode(FS * srcFs,FSPath & srcPath,FSNode * srcNode,FS * destFs,FSPath & destPath,bool move)1550 bool OperCFThread::CopyNode( FS* srcFs, FSPath& srcPath, FSNode* srcNode, FS* destFs, FSPath& destPath, bool move )
1551 {
1552 // XXX blame, blame. In tmp panel name has full path. Strip the path from the last item
1553 stripPathFromLastItem(destPath);
1554
1555 if ( srcNode->st.IsLnk() )
1556 {
1557 if ( !CopyLink( srcFs, srcPath, srcNode, destFs, destPath, move ) ) { return false; }
1558 }
1559 else if ( srcNode->st.IsDir() )
1560 {
1561 if ( !CopyDir( srcFs, srcPath, srcNode, destFs, destPath, move ) ) { return false; }
1562 }
1563 else
1564 {
1565 if ( !CopyFile( srcFs, srcPath, srcNode, destFs, destPath, move ) ) { return false; }
1566 }
1567
1568 return true;
1569 }
1570
Copy(FS * srcFs,FSPath & __srcPath,FSList * list,FS * destFs,FSPath & __destPath,cstrhash<bool,unicode_t> & resList)1571 bool OperCFThread::Copy( FS* srcFs, FSPath& __srcPath, FSList* list, FS* destFs, FSPath& __destPath, cstrhash<bool, unicode_t>& resList )
1572 {
1573 if ( list->Count() <= 0 ) { return true; }
1574
1575 FSPath srcPath = __srcPath;
1576 int srcPos = srcPath.Count();
1577 FSPath destPath = __destPath;
1578 int destPos = destPath.Count();
1579
1580 FSStat st;
1581 int ret_error;
1582 int res = destFs->Stat( __destPath, &st, &ret_error, Info() );
1583
1584 if ( res == -2 ) { return false; }
1585
1586 if ( res && !destFs->IsENOENT( ret_error ) )
1587 {
1588 RedMessage( _LT( "Can't copy to:\n" ), destFs->Uri( destPath ).GetUtf8(), bOk, destFs->StrError( ret_error ).GetUtf8() );
1589 return false;
1590 }
1591
1592 bool exist = ( res == 0 );
1593
1594
1595 if ( list->Count() > 1 )
1596 {
1597 //если файлов >1 то копировать можно только в каталог
1598 if ( !exist )
1599 {
1600 RedMessage( _LT( "Can't copy files, destination is not found:\n" ), destFs->Uri( __destPath ).GetUtf8(), bOk );
1601 return false;
1602 }
1603
1604 if ( !st.IsDir() )
1605 {
1606 RedMessage( _LT( "Destination is not directory:\n" ), destFs->Uri( __destPath ).GetUtf8(), bOk );
1607 return false;
1608 }
1609
1610 for ( FSNode* node = list->First(); node; node = node->next )
1611 {
1612 if ( Info()->Stopped() ) { return false; }
1613 srcPath.SetItemStr( srcPos, node->Name() );
1614 destPath.SetItemStr(destPos, node->Name() );
1615
1616 if ( !CopyNode( srcFs, srcPath, node, destFs, destPath, false ) ) { return false; }
1617
1618 resList[node->Name().GetUnicode()] = true;
1619 }
1620 }
1621 else
1622 {
1623 // 1 element
1624
1625 if ( exist && st.IsDir() )
1626 {
1627 destPath.SetItemStr(destPos, list->First()->Name());
1628 }
1629
1630 srcPath.SetItemStr( srcPos, list->First()->Name() );
1631
1632 if ( !CopyNode( srcFs, srcPath, list->First(), destFs, destPath, false ) ) { return false; }
1633
1634 resList[list->First()->Name().GetUnicode()] = true;
1635 };
1636
1637 return true;
1638 }
1639
CopyThreadFunc(OperThreadNode * node)1640 void CopyThreadFunc( OperThreadNode* node )
1641 {
1642 try
1643 {
1644 MutexLock lock( node->GetMutex() );
1645
1646 if ( !node->Data() ) { return; }
1647
1648 OperCFData* data = ( ( OperCFData* )node->Data() );
1649 OperCFThread thread( "Copy", data->Parent(), node );
1650
1651 clPtr<FS> srcFs = data->srcFs;
1652 FSPath srcPath = data->srcPath;
1653 clPtr<FS> destFs = data->destFs;
1654 FSPath destPath = data->destPath;
1655 clPtr<FSList> list = data->srcList;
1656
1657 lock.Unlock();//!!!
1658
1659 try
1660 {
1661 clPtr<cstrhash<bool, unicode_t> > resList = new cstrhash<bool, unicode_t>;
1662 thread.Copy( srcFs.Ptr(), srcPath, list.ptr(), destFs.Ptr(), destPath, *( resList.ptr() ) );
1663 lock.Lock(); //!!!
1664
1665 if ( !node->NBStopped() )
1666 {
1667 data->resList = resList;
1668 data->executed = true;
1669 }
1670 }
1671 catch ( cexception* ex )
1672 {
1673 lock.Lock(); //!!!
1674
1675 if ( !node->NBStopped() ) //обязательно надо проверить, иначе 'data' может быть неактуальной
1676 {
1677 data->errorString = ex->message();
1678 }
1679
1680 ex->destroy();
1681 }
1682 }
1683 catch ( cexception* ex )
1684 {
1685 fprintf( stderr, "ERR!!! Error exception in CopyThreadFunc - '%s'\n", ex->message() );
1686 ex->destroy();
1687 }
1688 catch ( ... )
1689 {
1690 fprintf( stderr, "ERR!!! Unhandled exception in CopyThreadFunc\n" );
1691 }
1692 }
1693
CopyFiles(clPtr<FS> srcFs,FSPath & srcPath,clPtr<FSList> list,clPtr<FS> destFs,FSPath & destPath,NCDialogParent * parent)1694 clPtr<cstrhash<bool, unicode_t> > CopyFiles( clPtr<FS> srcFs, FSPath& srcPath, clPtr<FSList> list, clPtr<FS> destFs, FSPath& destPath, NCDialogParent* parent )
1695 {
1696 CopyDialog dlg( parent );
1697 dlg.threadData.Clear();
1698 dlg.threadData.srcFs = srcFs;
1699 dlg.threadData.srcPath = srcPath;
1700 dlg.threadData.srcList = list;
1701
1702 dlg.threadData.destFs = destFs;
1703 dlg.threadData.destPath = destPath;
1704
1705 dlg.RunNewThread( "Copy", CopyThreadFunc, &dlg.threadData ); //может быть исключение
1706 dlg.Enable();
1707 dlg.Show();
1708 dlg.DoModal();
1709 dlg.StopThread();
1710 return dlg.threadData.resList;
1711 }
1712
1713
1714
1715
1716
1717 ///////////////////////////////////////////////// MOVE files /////////////////////////////////
1718
1719 /*
1720 0 - ok
1721 1 - need copy
1722 -1 - stop
1723 */
1724
MoveFile(FS * srcFs,FSPath & srcPath,FSNode * srcNode,FS * destFs,FSPath & destPath)1725 int OperCFThread::MoveFile( FS* srcFs, FSPath& srcPath, FSNode* srcNode, FS* destFs, FSPath& destPath )
1726 {
1727 if ( srcFs != destFs && !srcFs->Equal( destFs ) ) { return 1; }
1728
1729 if ( IsSameFile( srcFs, srcPath, &( srcNode->st ), destFs, destPath ) )
1730 {
1731 RedMessage( _LT( "Can't move file to itself:\n" ), srcFs->Uri( srcPath ).GetUtf8() );
1732 return -1;
1733 }
1734
1735 int ret_error;
1736
1737 FSStat st;
1738
1739 if ( !destFs->Stat( destPath, &st, &ret_error, Info() ) )
1740 {
1741 if ( !commitAll )
1742 {
1743 switch ( RedMessage( _LT( "Overwrite file?\n" ), destFs->Uri( destPath ).GetUtf8(),
1744 bOkAllNoCancel,
1745 destFs->StrError( ret_error ).GetUtf8() ) )
1746 {
1747 case CMD_ALL:
1748 commitAll = true; //no break
1749
1750 case CMD_OK:
1751 break;
1752
1753 case CMD_NO:
1754 return 0;
1755
1756 default:
1757 return -1;
1758 }
1759 }
1760
1761 if ( destFs->Delete( destPath, &ret_error, Info() ) )
1762 return RedMessage( _LT( "Can't delete the file:\n" ), destFs->Uri( destPath ).GetUtf8(),
1763 bSkipCancel, destFs->StrError( ret_error ).GetUtf8() ) == CMD_SKIP ? 0 : -1 ;
1764 }
1765
1766 if ( srcFs->Rename( srcPath, destPath, &ret_error, Info() ) )
1767 {
1768 if ( srcFs->IsEXDEV( ret_error ) ) { return 1; }
1769
1770 return RedMessage( _LT( "Can't rename the file:\n" ), srcFs->Uri( srcPath ).GetUtf8(), "\nto\n", destFs->Uri( destPath ).GetUtf8(),
1771 bSkipCancel, srcFs->StrError( ret_error ).GetUtf8() ) == CMD_SKIP ? 0 : -1;
1772
1773 }
1774
1775 return 0;
1776 }
1777
1778 /*
1779 0 - ok
1780 1 - need copy
1781 -1 - stop
1782 */
1783
MoveDir(FS * srcFs,FSPath & __srcPath,FSNode * srcNode,FS * destFs,FSPath & __destPath)1784 int OperCFThread::MoveDir( FS* srcFs, FSPath& __srcPath, FSNode* srcNode, FS* destFs, FSPath& __destPath )
1785 {
1786 if ( Info()->Stopped() ) { return -1; }
1787
1788 if ( srcFs != destFs ) { return 1; }
1789
1790 FSPath srcPath = __srcPath;
1791 int srcPos = srcPath.Count();
1792 FSPath destPath = __destPath;
1793 int destPos = destPath.Count();
1794
1795 if ( IsSameFile( srcFs, srcPath, &( srcNode->st ), destFs, destPath ) )
1796 {
1797 RedMessage( _LT( "Can't move directory to itself:\n" ), srcFs->Uri( __srcPath ).GetUtf8() );
1798 return -1;
1799 }
1800
1801 FSStat st;
1802 int ret_error;
1803
1804 if ( !destFs->Stat( destPath, &st, &ret_error, Info() ) )
1805 {
1806 if ( !st.IsDir() )
1807 {
1808 switch ( RedMessage( _LT( "Can't copy directory\n" ), srcFs->Uri( srcPath ).GetUtf8(), _LT( "to file" ), "\n", _LT( "Delete the file?" ), destFs->Uri( destPath ).GetUtf8(), bOkSkipCancel ) )
1809 {
1810 case CMD_CANCEL:
1811 return -1;
1812
1813 case CMD_SKIP:
1814 return 0;
1815 }
1816
1817 if ( !Unlink( destFs, destPath ) ) { return -1; }
1818 }
1819 else
1820 {
1821
1822 FSList list;
1823
1824 while ( true )
1825 {
1826 int ret = srcFs->ReadDir( &list, srcPath, &ret_error, Info() );
1827
1828 if ( ret == -2 ) { return -1; }
1829
1830 if ( !ret ) { break; }
1831
1832 switch ( RedMessage( _LT( "Can`t open directory:\n" ), srcFs->Uri( __srcPath ).GetUtf8(), bRetrySkipCancel, srcFs->StrError( ret_error ).GetUtf8() ) )
1833 {
1834 case CMD_SKIP:
1835 return 0;
1836
1837 case CMD_RETRY:
1838 continue;
1839
1840 default:
1841 return -1;
1842 }
1843 }
1844
1845 for ( FSNode* node = list.First(); node; node = node->next )
1846 {
1847 if ( Info()->Stopped() ) { return -1; }
1848
1849 srcPath.SetItemStr( srcPos, node->Name() );
1850 destPath.SetItemStr( destPos, node->Name() );
1851
1852 if ( !MoveFile( srcFs, srcPath, node, destFs, destPath ) ) { return -1; }
1853
1854 }
1855
1856 destFs->SetFileTime( destPath, srcNode->st.m_CreationTime, srcNode->st.m_LastWriteTime, srcNode->st.m_LastWriteTime, 0, Info() );
1857
1858 return RmDir( srcFs, srcPath ) ? 0 : -1;
1859 }
1860 }
1861
1862 if ( srcFs->Rename( srcPath, destPath, &ret_error, Info() ) )
1863 {
1864 if ( srcFs->IsEXDEV( ret_error ) ) { return 1; }
1865
1866 return RedMessage( _LT( "Can't rename the directory:\n" ), srcFs->Uri( srcPath ).GetUtf8(), "\nto\n", destFs->Uri( destPath ).GetUtf8(),
1867 bSkipCancel, srcFs->StrError( ret_error ).GetUtf8() ) == CMD_SKIP ? 0 : -1;
1868
1869 }
1870
1871 return 0;
1872 }
1873
MoveNode(FS * srcFs,FSPath & srcPath,FSNode * srcNode,FS * destFs,FSPath & destPath)1874 bool OperCFThread::MoveNode( FS* srcFs, FSPath& srcPath, FSNode* srcNode, FS* destFs, FSPath& destPath )
1875 {
1876
1877 if ( srcNode->st.IsLnk() )
1878 {
1879 int r = MoveFile( srcFs, srcPath, srcNode, destFs, destPath );
1880
1881 if ( r < 0 ) { return false; }
1882
1883 if ( r > 0 && !CopyLink( srcFs, srcPath, srcNode, destFs, destPath, true ) ) { return false; }
1884 }
1885 else if ( srcNode->st.IsDir() )
1886 {
1887 int r = MoveDir( srcFs, srcPath, srcNode, destFs, destPath );
1888
1889 if ( r < 0 ) { return false; }
1890
1891 if ( r > 0 && !CopyDir( srcFs, srcPath, srcNode, destFs, destPath, true ) ) { return false; }
1892
1893 }
1894 else
1895 {
1896 int r = MoveFile( srcFs, srcPath, srcNode, destFs, destPath );
1897
1898 if ( r < 0 ) { return false; }
1899
1900 if ( r > 0 && !CopyFile( srcFs, srcPath, srcNode, destFs, destPath, true ) ) { return false; }
1901 }
1902
1903 return true;
1904 }
1905
1906
Move(FS * srcFs,FSPath & __srcPath,FSList * list,FS * destFs,FSPath & __destPath)1907 bool OperCFThread::Move( FS* srcFs, FSPath& __srcPath, FSList* list, FS* destFs, FSPath& __destPath )
1908 {
1909 if ( list->Count() <= 0 ) { return true; }
1910
1911 FSPath srcPath = __srcPath;
1912 int srcPos = srcPath.Count();
1913 FSPath destPath = __destPath;
1914 int destPos = destPath.Count();
1915
1916 FSStat st;
1917 int ret_error;
1918 int r = destFs->Stat( __destPath, &st, &ret_error, Info() );
1919
1920 if ( r == -2 ) { return false; }
1921
1922
1923 if ( list->Count() > 1 )
1924 {
1925
1926 //если файлов >1 то копировать можно только в каталог
1927 if ( r )
1928 {
1929 RedMessage( _LT( "Can't move files, bad destination directory:\n" ), destFs->Uri( __destPath ).GetUtf8(), bOk, destFs->StrError( ret_error ).GetUtf8() );
1930 return false;
1931 }
1932
1933 if ( !st.IsDir() )
1934 {
1935 RedMessage( _LT( "Destination is not directory:\n" ), destFs->Uri( __destPath ).GetUtf8(), bOk );
1936 return false;
1937 }
1938
1939 for ( FSNode* node = list->First(); node; node = node->next )
1940 {
1941 srcPath.SetItemStr( srcPos, node->Name() );
1942 destPath.SetItemStr( destPos, node->Name() );
1943
1944 //printf("MOVE '%s'\n", srcPath.GetUtf8());
1945 if ( !MoveNode( srcFs, srcPath, node, destFs, destPath ) ) { return false; }
1946 }
1947
1948 }
1949 else
1950 {
1951 // 1 element
1952
1953 if ( r && !destFs->IsENOENT( ret_error ) )
1954 {
1955 RedMessage( _LT( "Can't move to:\n" ), destFs->Uri( destPath ).GetUtf8(), bOk, destFs->StrError( ret_error ).GetUtf8() );
1956 return false;
1957 }
1958
1959 if ( !r && st.IsDir() )
1960 {
1961 destPath.SetItemStr( destPos, list->First()->Name() );
1962 }
1963
1964 // FSNode* node = list->First();
1965
1966 srcPath.SetItemStr( srcPos, list->First()->Name() );
1967
1968 if ( !MoveNode( srcFs, srcPath, list->First(), destFs, destPath ) ) { return false; }
1969
1970 }
1971
1972 return true;
1973 }
1974
MoveThreadFunc(OperThreadNode * node)1975 void MoveThreadFunc( OperThreadNode* node )
1976 {
1977 try
1978 {
1979 MutexLock lock( node->GetMutex() );
1980
1981 if ( !node->Data() ) { return; }
1982
1983 OperCFData* data = ( ( OperCFData* )node->Data() );
1984 OperCFThread thread( "Copy", data->Parent(), node );
1985
1986 clPtr<FS> srcFs = data->srcFs;
1987 FSPath srcPath = data->srcPath;
1988 clPtr<FS> destFs = data->destFs;
1989 FSPath destPath = data->destPath;
1990 clPtr<FSList> list = data->srcList;
1991
1992 lock.Unlock();//!!!
1993
1994 try
1995 {
1996 thread.Move( srcFs.Ptr(), srcPath, list.ptr(), destFs.Ptr(), destPath );
1997 }
1998 catch ( cexception* ex )
1999 {
2000 lock.Lock(); //!!!
2001
2002 if ( !node->NBStopped() ) //обязательно надо проверить, иначе 'data' может быть неактуальной
2003 {
2004 data->errorString = ex->message();
2005 }
2006
2007 ex->destroy();
2008 }
2009 }
2010 catch ( cexception* ex )
2011 {
2012 fprintf( stderr, "ERR!!! Error exception in MoveThreadFunc - '%s'\n", ex->message() );
2013 ex->destroy();
2014 }
2015 catch ( ... )
2016 {
2017 fprintf( stderr, "ERR!!! Unhandled exception in MoveThreadFunc\n" );
2018 }
2019 }
2020
MoveFiles(clPtr<FS> srcFs,FSPath & srcPath,clPtr<FSList> list,clPtr<FS> destFs,FSPath & destPath,NCDialogParent * parent)2021 bool MoveFiles( clPtr<FS> srcFs, FSPath& srcPath, clPtr<FSList> list, clPtr<FS> destFs, FSPath& destPath, NCDialogParent* parent )
2022 {
2023 CopyDialog dlg( parent );
2024 dlg.threadData.Clear();
2025 dlg.threadData.srcFs = srcFs;
2026 dlg.threadData.srcPath = srcPath;
2027 dlg.threadData.srcList = list;
2028
2029 dlg.threadData.destFs = destFs;
2030 dlg.threadData.destPath = destPath;
2031
2032
2033 dlg.RunNewThread( "Move", MoveThreadFunc, &dlg.threadData ); //может быть исключение
2034 dlg.Enable();
2035 dlg.Show();
2036 dlg.DoModal();
2037 dlg.StopThread();
2038 return dlg.threadData.errorString.IsEmpty();
2039 }
2040
2041