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