/* * Part of WCM Commander * https://github.com/corporateshark/WCMCommander * wcm@linderdaum.com */ #ifdef _WIN32 # include #endif #include #include "fileopers.h" #include "smblogon.h" #include "ftplogon.h" #include "sftpdlg.h" #include "ltext.h" #include "vfs-tmp.h" enum { CMD_ALL = 200, CMD_SKIP, CMD_SKIPALL, CMD_RETRY }; #define MkDirMode (S_IRWXU | S_IRGRP | S_IROTH | S_IXGRP | S_IXOTH) /* //for ltext searcher _LT("DB>Delete") _LT("DB>All") _LT("DB>Skip") _LT("DB>Retry") _LT("DB>Skip All") _LT("DB>All") _LT("DB>Yes") _LT("DB>No") */ static ButtonDataNode bDeleteAllSkipCancel[] = { {"&Delete", CMD_OK}, { "&All", CMD_ALL}, { "&Skip", CMD_SKIP}, {"&Cancel", CMD_CANCEL}, {0, 0}}; static ButtonDataNode bRetrySkipCancel[] = { { "&Retry", CMD_RETRY}, { "&Skip", CMD_SKIP}, {"&Cancel", CMD_CANCEL}, {0, 0}}; static ButtonDataNode bRetrySkipSkipallCancel[] = { { "&Retry", CMD_RETRY}, { "&Skip", CMD_SKIP}, { "Skip &All", CMD_SKIPALL}, {"&Cancel", CMD_CANCEL}, {0, 0}}; static ButtonDataNode bOkSkipCancel[] = { { "O&k", CMD_OK}, { "&Skip", CMD_SKIP}, {"&Cancel", CMD_CANCEL}, {0, 0}}; static ButtonDataNode bSkipCancel[] = { { "&Skip" , CMD_SKIP}, {"&Cancel", CMD_CANCEL}, {0, 0}}; static ButtonDataNode bSkipSkipallCancel[] = { { "&Skip", CMD_SKIP}, { "Skip &All", CMD_SKIPALL}, {"&Cancel", CMD_CANCEL}, {0, 0}}; //static ButtonDataNode bOk[] = { { " O&k ", CMD_OK}, {0,0}}; static ButtonDataNode bOkAllNoCancel[] = { { "O&k", CMD_OK}, { "&All", CMD_ALL}, { "&No", CMD_NO}, {"&Cancel", CMD_CANCEL}, {0, 0}}; ///////////////////////// smb callback objs struct SmbLogonCBData { FSSmbParam* volatile param; NCDialogParent* volatile parent; }; #ifdef LIBSMBCLIENT_EXIST int SmbLogonOperCallback( void* cbData ) { SmbLogonCBData* data = ( SmbLogonCBData* ) cbData; bool ret = GetSmbLogon( data->parent, *( data->param ) ); return ret ? 1 : 0; } #endif bool OF_FSCInfo::SmbLogon( FSSmbParam* a ) { #ifdef LIBSMBCLIENT_EXIST MutexLock lock( _node->GetMutex() ); if ( _node->NBStopped() ) { return false; } OperData* p = ( OperData* ) _node->Data(); if ( !p ) { return false; } SmbLogonCBData data; data.param = a; data.parent = p->Parent(); lock.Unlock(); return _node->CallBack( SmbLogonOperCallback, &data ) > 0; #else return false; #endif } /////////////////////////ftp callback objs struct FtpLogonCBData { FSFtpParam* volatile param; NCDialogParent* volatile parent; }; int FtpLogonOperCallback( void* cbData ) { FtpLogonCBData* data = ( FtpLogonCBData* ) cbData; bool ret = GetFtpLogon( data->parent, *( data->param ) ); return ret ? 1 : 0; } bool OF_FSCInfo::FtpLogon( FSFtpParam* a ) { MutexLock lock( _node->GetMutex() ); if ( _node->NBStopped() ) { return false; } OperData* p = ( OperData* ) _node->Data(); if ( !p ) { return false; } FtpLogonCBData data; data.param = a; data.parent = p->Parent(); lock.Unlock(); return _node->CallBack( FtpLogonOperCallback, &data ) > 0; } ///////////////////////////// prompt callback int PromptOperCallback( void* cbData ) { PromptCBData* data = ( PromptCBData* ) cbData; bool ret = GetPromptAnswer( data ); return ret ? 1 : 0; } bool OF_FSCInfo::Prompt( const unicode_t* header, const unicode_t* message, FSPromptData* prompts, int count ) { MutexLock lock( _node->GetMutex() ); if ( _node->NBStopped() ) { return false; } OperData* p = ( OperData* ) _node->Data(); if ( !p ) { return false; } PromptCBData data; data.header = header; data.message = message; data.prompts = prompts; data.count = count; data.parent = p->Parent(); lock.Unlock(); return _node->CallBack( PromptOperCallback, &data ) > 0; } bool OF_FSCInfo::Stopped() { MutexLock lock( _node->GetMutex() ); return _node->NBStopped(); }; OF_FSCInfo::~OF_FSCInfo() {}; OperFileThread::~OperFileThread() {} OperRDData::~OperRDData() {} class OperRDThread: public OperFileThread { clPtr fs; FSPath path; public: OperRDThread( const char* opName, NCDialogParent* par, OperThreadNode* n, clPtr f, FSPath& p ) : OperFileThread( opName, par, n ), fs( f ), path( p ) {} virtual void Run(); virtual ~OperRDThread(); }; void OperRDThread::Run() { if ( !fs.Ptr() ) { return; } int n = 8; int ret_err; int havePostponedStatError = 0; FSString postponedStrError; while ( true ) { if ( !( fs->Flags() & FS::HAVE_SYMLINK ) ) { break; } FSStat st; // if path is inaccessible, try .. path. Throw the exception later // This makes panel at least having some valid folder while ( fs->Stat( path, &st, &ret_err, Info() ) ) { havePostponedStatError = 1; postponedStrError = fs->StrError( ret_err ); if ( !path.IsAbsolute() || path.Count() <= 1 || !path.Pop() ) { throw_msg( "%s", postponedStrError.GetUtf8() ); } } // yell immediately if the path is inaccessible (orig behavior) //if (fs->Stat(path, &st, &ret_err, Info())) // throw_msg("%s", fs->StrError(ret_err).GetUtf8()); if ( !st.IsLnk() ) { break; } n--; if ( n < 0 ) { throw_msg( "too many symbolic links '%s'", path.GetUtf8() ); } path.Pop(); if ( !ParzeLink( path, st.link ) ) { throw_msg( "invalid symbolic link '%s'", path.GetUtf8() ); } } clPtr list = new FSList; int havePostponedReadError = 0; // if directory is not readable, try .. path. Throw the exception later // "Stat" call above does not catch this: it checks only folder existence, but not accessibilly while ( fs->ReadDir( list.ptr(), path, &ret_err, Info() ) ) { havePostponedReadError = 1; postponedStrError = fs->StrError( ret_err ); if ( !path.IsAbsolute() || path.Count() <= 1 || !path.Pop() ) { throw_msg( "%s", postponedStrError.GetUtf8() ); } } // yell immediately if the dir is unreadable (orig behavior) //int ret = fs->ReadDir(list.ptr(), path, &ret_err, Info()); //if (ret) // throw_msg("%s", fs->StrError(ret_err).GetUtf8()); FSStatVfs vst; fs->StatVfs( path, &vst, &ret_err, Info() ); MutexLock lock( Node().GetMutex() ); //!!! if ( Node().NBStopped() ) { return; } OperRDData* data = ( ( OperRDData* )Node().Data() ); data->list = list; data->path = path; data->executed = true; data->vst = vst; if ( havePostponedReadError || havePostponedStatError ) { data->nonFatalErrorString = postponedStrError; } } OperRDThread::~OperRDThread() {} void ReadDirThreadFunc( OperThreadNode* node ) { try { MutexLock lock( node->GetMutex() ); if ( !node->Data() ) { return; } OperRDData* data = ( ( OperRDData* )node->Data() ); //dbg_printf("ReadDirThreadFunc path=%s",data->path.GetUtf8()); OperRDThread thread( "panel::chdir", data->Parent(), node, data->fs, data->path ); lock.Unlock();//!!! try { thread.Run(); } catch ( cexception* ex ) { lock.Lock(); //!!! if ( !node->NBStopped() ) //обязательно надо проверить, иначе 'data' может быть неактуальной { data->errorString = ex->message(); } ex->destroy(); } } catch ( cexception* ex ) { fprintf( stderr, "ERR!!! Error exception in ReadDirThreadFunc - '%s'\n", ex->message() ); ex->destroy(); } catch ( ... ) { fprintf( stderr, "ERR!!! Unhandled exception in ReadDirThreadFunc\n" ); } } ////////////////////////////////////////// Common file operations enum InfoSignal { INFO_NEXTFILE = 2 }; class OperCFData: public OperData { public: volatile bool executed; clPtr srcFs; //??volatile FSPath srcPath; //??volatile clPtr srcList; //??volatile clPtr destFs; //??volatile FSPath destPath; //??volatile FSString errorString; //??volatile clPtr > resList; //??volatile Mutex infoMutex; volatile bool pathChanged; volatile uint64_t infoCount; FSString infoSrcUri; //??volatile FSString infoDstUri; //??volatile volatile bool progressChanged; volatile int64_t infoSize; volatile int64_t infoProgress; volatile unsigned infoMs; //for calculating copy speed volatile int64_t infoBytes; // OperCFData( NCDialogParent* p ) : OperData( p ), executed( false ), pathChanged( false ), infoCount( 0 ), progressChanged( false ), infoSize( 0 ), infoProgress( 0 ), infoMs( 0 ), infoBytes( 0 ) {} void Clear() { executed = false; errorString.Clear(); srcList.clear(); pathChanged = false; // uint64_t infoCount = 0; infoSrcUri.Clear(); infoDstUri.Clear(); progressChanged = false; infoSize = 0; infoProgress = 0; } virtual ~OperCFData(); }; OperCFData::~OperCFData() {} class OperCFThread: public OperFileThread { volatile bool commitAll; volatile bool skipNonRegular; enum { BSIZE = 1024 * 512, STARTSIZE = 1024 * 64 }; char* _buffer; public: OperCFThread( const char* opName, NCDialogParent* par, OperThreadNode* n ) : OperFileThread( opName, par, n ), commitAll( false ), skipNonRegular( false ), _buffer( 0 ) { _buffer = new char[BSIZE]; } void CreateDirectory( FS* fs, FSPath& path ); //throws bool Unlink ( FS* fs, FSPath& path, bool* skipAll = 0 ); bool RmDir ( FS* fs, FSPath& path, bool* skipAll = 0 ); bool DeleteFile ( FS* fs, FSPath& path ); bool DeleteDir ( FS* fs, FSPath& path ); bool DeleteList ( FS* fs, FSPath& _path, FSList& list ); //from и to - эффект cptr !!! bool SendCopyNextFileInfo( FSString from, FSString to ); bool SendProgressInfo( int64_t size, int64_t progress, int64_t bytes ); bool CopyLink( FS* srcFs, FSPath& srcPath, FSNode* srcNode, FS* destFs, FSPath& path, bool move ); // XXX CopyFile/MoveFile are #define'd in winbase.h bool CopyFile( FS* srcFs, FSPath& srcPath, FSNode* srcNode, FS* destFs, FSPath& destPath, bool move ); bool CopyDir( FS* srcFs, FSPath& __srcPath, FSNode* srcNode, FS* destFs, FSPath& __destPath, bool move ); bool CopyNode( FS* srcFs, FSPath& srcPath, FSNode* srcNode, FS* destFs, FSPath& destPath, bool move ); bool Copy( FS* srcFs, FSPath& __srcPath, FSList* list, FS* destFs, FSPath& __destPath, cstrhash& resList ); int MoveFile( FS* srcFs, FSPath& srcPath, FSNode* srcNode, FS* destFs, FSPath& destPath ); int MoveDir( FS* srcFs, FSPath& __srcPath, FSNode* srcNode, FS* destFs, FSPath& __destPath ); bool MoveNode( FS* srcFs, FSPath& srcPath, FSNode* srcNode, FS* destFs, FSPath& destPath ); bool Move( FS* srcFs, FSPath& __srcPath, FSList* list, FS* destFs, FSPath& __destPath ); virtual ~OperCFThread(); }; OperCFThread::~OperCFThread() { if ( _buffer ) { delete [] _buffer; } } void OperCFThread::CreateDirectory( FS* fs, FSPath& path ) { int ret_err; if ( fs->MkDir( path, 0777, &ret_err, Info() ) ) { throw_msg( "%s", fs->StrError( ret_err ).GetUtf8() ); } } class SimpleCFThreadWin: public NCDialog { public: OperCFData threadData; SimpleCFThreadWin( NCDialogParent* parent, const char* name ) : NCDialog( ::createDialogAsChild, 0, parent, utf8_to_unicode( name ).data(), bListCancel ), threadData( parent ) {} virtual void OperThreadStopped(); virtual ~SimpleCFThreadWin(); }; void SimpleCFThreadWin::OperThreadStopped() { if ( !threadData.errorString.IsEmpty() ) { NCMessageBox( ( NCDialogParent* )Parent(), _LT( "Create directory" ), threadData.errorString.GetUtf8(), true ); EndModal( 0 ); return; } EndModal( 100 ); } SimpleCFThreadWin::~SimpleCFThreadWin() {} /////////////////////////////////////////////////////// MkDir void MkDirThreadFunc( OperThreadNode* node ) { try { MutexLock lock( node->GetMutex() ); if ( !node->Data() ) { return; } OperCFData* data = ( ( OperCFData* )node->Data() ); OperCFThread thread( "create directory", data->Parent(), node ); clPtr fs = data->srcFs; FSPath path = data->srcPath; lock.Unlock();//!!! try { thread.CreateDirectory( fs.Ptr(), path ); } catch ( cexception* ex ) { lock.Lock(); //!!! if ( !node->NBStopped() ) //обязательно надо проверить, иначе 'data' может быть неактуальной { data->errorString = ex->message(); } ex->destroy(); } } catch ( cexception* ex ) { fprintf( stderr, "ERR!!! Error exception in MkDirThreadFunc - '%s'\n", ex->message() ); ex->destroy(); } catch ( ... ) { fprintf( stderr, "ERR!!! Unhandled exception in MkDirThreadFunc\n" ); } } bool MkDir( clPtr f, FSPath& p, NCDialogParent* parent ) { SimpleCFThreadWin dlg( parent, _LT( "Create directory" ) ); dlg.threadData.Clear(); dlg.threadData.srcFs = f; dlg.threadData.srcPath = p; dlg.RunNewThread( "Create directory", MkDirThreadFunc, &dlg.threadData ); //может быть исключение dlg.Enable(); dlg.Show(); dlg.DoModal(); dlg.StopThread(); return dlg.threadData.errorString.IsEmpty(); } ///////////////////////////////////////////////////// DELETE //возвращает true если можно продолжать процесс bool OperCFThread::Unlink( FS* fs, FSPath& path, bool* skipAll ) { int ret_err; while ( fs->Delete( path, &ret_err, Info() ) ) { if ( skipAll && *skipAll ) { return true; } switch ( RedMessage( _LT( "Can`t delete file:\n" ), fs->Uri( path ).GetUtf8(), skipAll ? bRetrySkipSkipallCancel : bRetrySkipCancel, fs->StrError( ret_err ).GetUtf8() ) ) { case CMD_SKIPALL: if ( skipAll ) { *skipAll = true; } //no break case CMD_SKIP: return true; case CMD_RETRY: continue; default: return false; } } return true; } bool OperCFThread::RmDir( FS* fs, FSPath& path, bool* skipAll ) { int ret_err; while ( fs->RmDir( path, &ret_err, Info() ) ) { if ( skipAll && *skipAll ) { return true; } switch ( RedMessage( _LT( "Can`t delete directory:\n" ) , fs->Uri( path ).GetUtf8(), skipAll ? bRetrySkipSkipallCancel : bRetrySkipCancel, fs->StrError( ret_err ).GetUtf8() ) ) { case CMD_SKIPALL: if ( skipAll ) { *skipAll = true; } //no break case CMD_SKIP: return true; case CMD_RETRY: continue; default: return false; } } return true; } bool OperCFThread::DeleteFile( FS* fs, FSPath& path ) //return true if not concelled { if ( Info()->Stopped() ) { return false; } if ( !commitAll ) { switch ( RedMessage( _LT( "Do you want to delete file?\n" ), fs->Uri( path ).GetUtf8(), bDeleteAllSkipCancel ) ) { case CMD_SKIP: return true; case CMD_ALL: commitAll = true; break; case CMD_OK: break; default: return false; } } return Unlink( fs, path ); //skip all??? } bool OperCFThread::DeleteDir( FS* fs, FSPath& path ) { if ( Info()->Stopped() ) { return false; } FSList list; while ( true ) { int ret_err; int ret = fs->ReadDir( &list, path, &ret_err, Info() ); if ( ret == -2 ) { return false; } if ( !ret ) { break; } switch ( RedMessage( _LT( "Can`t open directory:\n" ), fs->Uri( path ).GetUtf8(), bRetrySkipCancel, fs->StrError( ret_err ).GetUtf8() ) ) { case CMD_SKIP: return true; case CMD_RETRY: continue; default: return false; } } return DeleteList( fs, path, list ); } bool OperCFThread::DeleteList( FS* fs, FSPath& _path, FSList& list ) { if ( Info()->Stopped() ) { return false; } FSPath path = _path; int cnt = path.Count(); for ( FSNode* node = list.First(); node; node = node->next ) { if ( node->extType ) { continue; } path.SetItemStr( cnt, node->Name() ); if ( node->IsDir() && !node->st.IsLnk() ) { if ( !DeleteDir( fs, path ) ) { return false; } if ( !RmDir( fs, path ) ) { return false; } continue; } if ( !DeleteFile( fs, path ) ) { return false; } } return true; } void DeleteThreadFunc( OperThreadNode* node ) { try { MutexLock lock( node->GetMutex() ); if ( !node->Data() ) { return; } OperCFData* data = ( ( OperCFData* )node->Data() ); OperCFThread thread( "Delete", data->Parent(), node ); clPtr fs = data->srcFs; FSPath path = data->srcPath; clPtr list = data->srcList; lock.Unlock();//!!! try { if ( list.ptr() ) { thread.DeleteList( fs.Ptr(), path, *( list.ptr() ) ); } } catch ( cexception* ex ) { lock.Lock(); //!!! if ( !node->NBStopped() ) //обязательно надо проверить, иначе 'data' может быть неактуальной { data->errorString = ex->message(); } ex->destroy(); } } catch ( cexception* ex ) { fprintf( stderr, "ERR!!! Error exception in DeleteThreadFunc - '%s'\n", ex->message() ); ex->destroy(); } catch ( ... ) { fprintf( stderr, "ERR!!! Unhandled exception in DeleteThreadFunc\n" ); } } bool DeleteList( clPtr f, FSPath& p, clPtr list, NCDialogParent* parent ) { SimpleCFThreadWin dlg( parent, _LT( "Delete" ) ); dlg.threadData.Clear(); dlg.threadData.srcFs = f; dlg.threadData.srcPath = p; dlg.threadData.srcList = list; dlg.RunNewThread( "Delete", DeleteThreadFunc, &dlg.threadData ); //может быть исключение dlg.Enable(); dlg.Show(); dlg.DoModal(); dlg.StopThread(); return dlg.threadData.errorString.IsEmpty(); } /////////////////////////////////////////////////////////////// Copy bool OperCFThread::SendCopyNextFileInfo( FSString from, FSString to ) { MutexLock lock( Node().GetMutex() ); if ( Node().NBStopped() ) { return false; } OperCFData* data = ( ( OperCFData* )Node().Data() ); if ( !data ) { return false; } data->infoCount++; data->infoSrcUri = from; data->infoDstUri = to; data->pathChanged = true; if ( !WinThreadSignal( INFO_NEXTFILE ) ) { return false; } data->infoProgress = 0; return true; } namespace wal { extern unsigned GetTickMiliseconds(); }; bool OperCFThread::SendProgressInfo( int64_t size, int64_t progress, int64_t bytes ) { MutexLock lock( Node().GetMutex() ); if ( Node().NBStopped() ) { return false; } OperCFData* data = ( ( OperCFData* )Node().Data() ); if ( !data ) { return false; } data->infoSize = size; data->infoProgress = progress; data->infoBytes += bytes; data->infoMs = GetTickMiliseconds(); data->progressChanged = true; if ( !WinThreadSignal( INFO_NEXTFILE ) ) { return false; } return true; } OperFileNameWin::OperFileNameWin( Win* parent, int ccount ) : Win( Win::WT_CHILD, 0, parent, 0 ) , _ccount( ccount ) { wal::GC gc( this ); cpoint size = GetStaticTextExtent( gc, ABCString, GetFont() ); size.x = size.x / ABCStringLen * ccount; if ( size.x > 500 ) { size.x = 550; } SetLSize( LSize( size ) ); } void OperFileNameWin::SetText( const unicode_t* s ) { text = new_unicode_str( s ); Invalidate(); } void OperFileNameWin::Paint( wal::GC& gc, const crect& paintRect ) { crect rect = ClientRect(); gc.SetFillColor( UiGetColor( uiBackground, 0, 0, 0xFFFFFF ) ); gc.FillRect( rect ); gc.SetTextColor( UiGetColor( uiColor, 0, 0, 0 ) ); gc.Set( GetFont() ); unicode_t* p = text.data(); if ( p ) { int l = unicode_strlen( p ); if ( l > _ccount ) { p += l - _ccount; } DrawStaticText( gc, 0, 0, p ); } } OperFileNameWin::~OperFileNameWin() {}; class NCNumberWin: public Win { int64_t _num; public: NCNumberWin( Win* parent, int width = 10 ) : Win( Win::WT_CHILD, 0, parent, 0 ), _num( 0 ) { wal::GC gc( this ); cpoint size = GetStaticTextExtent( gc, ABCString, GetFont() ); size.x = size.x / ABCStringLen * width; SetLSize( LSize( size ) ); } void SetNumber( int64_t n ) { if ( _num == n ) { return; } _num = n; Invalidate(); } void Paint( wal::GC& gc, const crect& paintRect ); virtual ~NCNumberWin(); }; void NCNumberWin::Paint( wal::GC& gc, const crect& paintRect ) { crect rect = ClientRect(); gc.SetFillColor( UiGetColor( uiBackground, 0, 0, 0xFFFFFF ) ); gc.FillRect( rect ); gc.SetTextColor( UiGetColor( uiColor, 0, 0, 0 ) ); gc.Set( GetFont() ); unicode_t c[32]; unicode_t buf[32]; unicode_t* s = buf; int64_t n = _num; bool minus = n < 0; if ( minus ) { n = -n; } int i = 0; for ( ; n > 0; i++, n /= 10 ) { c[i] = char( n % 10 ) + '0'; } if ( minus ) { *s = '-'; s++; } if ( i == 0 ) {*s = '0'; s++; }; for ( i--; i >= 0; i-- ) { *( s++ ) = c[i]; } *s = 0; gc.TextOut( 0, 0, buf ); } NCNumberWin::~NCNumberWin() {} class NCProgressWin: public Win { int64_t _from, _to; int64_t _num; int64_t _lastWidth, _lastPos; public: NCProgressWin( Win* parent ) : Win( Win::WT_CHILD, 0, parent, 0 ), _from( 0 ), _to( 0 ), _num( 0 ), _lastWidth( 0 ), _lastPos( 0 ) { cpoint size; size.x = 10; size.y = 16; LSize ls( size ); ls.x.maximal = 1000; SetLSize( ls ); } void SetData( int64_t from, int64_t to, int64_t num ) { if ( _from == from && _to == to && _num == num ) { return; } bool diapazonChanged = _from != from || _to != to; _from = from; _to = to; _num = num; if ( _num < _from || _to <= _from ) { return; } int64_t size = _to - _from; int64_t n = ( _lastWidth * _num ) / size; if ( diapazonChanged || _lastPos != n ) { Invalidate(); } } virtual void Paint( wal::GC& gc, const crect& paintRect ); virtual ~NCProgressWin(); }; inline unsigned MidAB( int a, int b, int i, int n ) { return n > 1 ? a + ( ( b - a ) * i ) / ( n - 1 ) : a; } static void FillHorisont( wal::GC& gc, crect rect, unsigned a, unsigned b ) { if ( rect.IsEmpty() ) { return; } unsigned ar = a & 0xFF, ag = ( a >> 8 ) & 0xFF, ab = ( a >> 16 ) & 0xFF, br = b & 0xFF, bg = ( b >> 8 ) & 0xFF, bb = ( b >> 16 ) & 0xFF; int h = rect.Height(); int x1 = rect.left, x2 = rect.right; if ( h <= 0 || x1 >= x2 ) { return; } for ( int i = 0; i < h; i++ ) { unsigned color = ( MidAB( ar, br, i, h ) & 0xFF ) + ( ( MidAB( ag, bg, i, h ) & 0xFF ) << 8 ) + ( ( MidAB( ab, bb, i, h ) & 0xFF ) << 16 ); gc.SetLine( color ); gc.MoveTo( x1, rect.top + i ); gc.LineTo( x2, rect.top + i ); } } void NCProgressWin::Paint( wal::GC& gc, const crect& paintRect ) { crect rect = ClientRect(); int w = rect.Width(); Draw3DButtonW2( gc, rect, 0x808080, false ); rect.Dec(); rect.Dec(); w -= 2; if ( !( _num < _from || _to <= _from || w <= 0 ) ) { int64_t size = _to - _from; int n = int( ( w * _num ) / size ); crect r = rect; r.right = n; unsigned color = 0xA00000; unsigned bColor = ColorTone( color, -80 ), aColor = ColorTone( color, +80 ); FillHorisont( gc, r, aColor, bColor ); _lastWidth = w; _lastPos = n; rect.left += n; } unsigned color = 0xB0B0B0; unsigned bColor = ColorTone( color, +50 ), aColor = ColorTone( color, -50 ); FillHorisont( gc, rect, aColor, bColor ); } NCProgressWin::~NCProgressWin() {} class CopyDialog: public SimpleCFThreadWin { Layout _layout; StaticLine _text1; StaticLine _text2; StaticLine _text3; OperFileNameWin _from, _to; NCNumberWin _countWin; NCProgressWin _progressWin; StaticLine _speedStr; enum { SPEED_NODE_COUNT = 10 }; struct SpeedNode { unsigned deltaMs; int64_t bytes; SpeedNode(): deltaMs( 0 ), bytes( 0 ) {} } _speedList[SPEED_NODE_COUNT]; unsigned _lastMs; public: CopyDialog( NCDialogParent* parent, bool move = false ) : SimpleCFThreadWin( parent, move ? _LT( "Move" ) : _LT( "Copy" ) ), _layout( 7, 2 ), _text1( 0, this, utf8_to_unicode( move ? _LT( "Moving the file" ) : _LT( "Copying the file" ) ).data() ), _text2( 0, this, utf8_to_unicode( _LT( "to" ) ).data() ), _text3( 0, this, utf8_to_unicode( _LT( "Files processed" ) ).data() ), _from( this ), _to( this ), _countWin( this ), _progressWin( this ), _speedStr( uiValue, this, 0, 0, StaticLine::LEFT, 10 ), _lastMs( GetTickMiliseconds() ) { _layout.AddWin( &_text1, 0, 0, 0, 1 ); _layout.AddWin( &_from, 1, 0, 1, 1 ); _layout.AddWin( &_text2, 2, 0, 2, 1 ); _layout.AddWin( &_to, 3, 0, 3, 1 ); _layout.AddWin( &_progressWin, 4, 0, 4, 1 ); _layout.AddWin( &_text3, 5, 0 ); _layout.AddWin( &_countWin, 5, 1 ); _layout.AddWin( &_speedStr, 6, 0 ); _text1.Show(); _text1.Enable(); _text2.Show(); _text2.Enable(); _text3.Show(); _text3.Enable(); _from.Show(); _from.Enable(); _to.Show(); _to.Enable(); _countWin.Show(); _countWin.Enable(); _progressWin.Show(); _progressWin.Enable(); _speedStr.Show(); _speedStr.Enable(); AddLayout( &_layout ); SetTimer( 1, 1000 ); SetPosition(); } virtual void OperThreadSignal( int info ); virtual void EventTimer( int tid ); virtual ~CopyDialog(); }; #define GIG (int64_t(1024)*1024*1024) #define MEG (int64_t(1024)*1024) #define KIL (int64_t(1024)) static char* GetSmallPrintableSpeedStr( char buf[64], int64_t size ) { char str[16]; str[0] = 0; int64_t num = size; int mod = 0; if ( num >= GIG ) //:) { mod = ( num % GIG ) / ( GIG / 10 ); num /= GIG; str[0] = ' '; str[1] = 'G'; str[2] = 'b'; str[3] = '/'; str[4] = 's'; str[5] = 0; } else if ( num >= MEG ) { mod = ( num % MEG ) / ( MEG / 10 ); num /= MEG; str[0] = ' '; str[1] = 'M'; str[2] = 'b'; str[3] = '/'; str[4] = 's'; str[5] = 0; } else if ( num >= KIL ) { mod = ( num % KIL ) / ( KIL / 10 ); num /= KIL; str[0] = ' '; str[1] = 'K'; str[2] = 'b'; str[3] = '/'; str[4] = 's'; str[5] = 0; } else { mod = -1; } char dig[64]; char* t = unsigned_to_char( num, dig ); if ( mod >= 0 ) { t--; t[0] = '.'; t[1] = mod + '0'; t[2] = 0; } char* us = buf; for ( char* s = dig; *s; s++ ) { *( us++ ) = *s; } for ( char* t = str; *t; t++ ) { *( us++ ) = *t; } *us = 0; return buf; } void CopyDialog::EventTimer( int tid ) { if ( tid == 1 ) { unsigned ms = 0; int64_t bytes = 0; { MutexLock lock( &threadData.infoMutex ); ms = threadData.infoMs; bytes = threadData.infoBytes; threadData.infoBytes = 0; lock.Unlock(); } //shift int i; for ( i = SPEED_NODE_COUNT - 1; i > 0; i-- ) { _speedList[i] = _speedList[i - 1]; } _speedList[0].bytes = bytes; if ( bytes > 0 ) { unsigned n = ms - _lastMs; _speedList[0].deltaMs = n > 1000000 ? 0 : n; _lastMs = ms; } else { _speedList[0].deltaMs = 0; } int64_t sumBytes = 0; int64_t sumMs = 0; for ( i = 0; i < SPEED_NODE_COUNT; i++ ) { sumMs += _speedList[i].deltaMs; sumBytes += _speedList[i].bytes; } int64_t speed = 0; if ( sumMs > 0 && sumBytes > 0 ) { speed = ( sumBytes * 1000 ) / sumMs; } char buf[64]; _speedStr.SetText( utf8_to_unicode( GetSmallPrintableSpeedStr( buf, speed ) ).data() ); return; } } void CopyDialog::OperThreadSignal( int info ) { if ( info == INFO_NEXTFILE ) { MutexLock lock( &threadData.infoMutex ); if ( threadData.pathChanged ) { _from.SetText( threadData.infoSrcUri.GetUnicode() ); _to.SetText( threadData.infoDstUri.GetUnicode() ); _countWin.SetNumber( threadData.infoCount ); threadData.pathChanged = false; } if ( threadData.progressChanged ) { _progressWin.SetData( 0, threadData.infoSize, threadData.infoProgress ); threadData.progressChanged = false; } } } CopyDialog::~CopyDialog() {} static bool IsSameFile( FS* srcFs, FSPath& srcPath, FSStat* srcStat, FS* destFs, FSPath& destPath ) { if ( srcFs->Type() != destFs->Type() ) { return false; } #ifndef _WIN32 if ( destFs->Type() == FS::SYSTEM ) { FSStat st; return ( !destFs->Stat( destPath, &st, 0, 0 ) && srcStat->dev == st.dev && srcStat->ino == st.ino ); } #endif FSString s = destFs->Uri( destPath ); return !srcFs->Uri( srcPath ).Cmp( s ); } bool OperCFThread::CopyLink( FS* srcFs, FSPath& srcPath, FSNode* srcNode, FS* destFs, FSPath& destPath, bool move ) { ASSERT( srcNode->st.IsLnk() ); if ( IsSameFile( srcFs, srcPath, &( srcNode->st ), destFs, destPath ) ) { RedMessage( _LT( "Can't copy link to itself:\n" ) , srcFs->Uri( srcPath ).GetUtf8() ); return false; } int ret_err; while ( destFs->Symlink( destPath, srcNode->st.link, &ret_err, Info() ) && !skipNonRegular ) switch ( RedMessage( _LT( "Can't create symbolic link:\n" ), destFs->Uri( destPath ).GetUtf8(), "to\n", srcNode->st.link.GetUtf8(), bSkipSkipallCancel, destFs->StrError( ret_err ).GetUtf8() ) ) { case CMD_CANCEL: return false; case CMD_SKIPALL: skipNonRegular = true; case CMD_SKIP: return true; } return !move || Unlink( srcFs, srcPath ); } //#define BUFSIZE (1024*512) //(1024*64) //inline FSString Err(FS *fs, int err){ return fs->StrError(err); } bool OperCFThread::CopyFile( FS* srcFs, FSPath& srcPath, FSNode* srcNode, FS* destFs, FSPath& destPath, bool move ) { if ( !srcNode->st.IsReg() && !skipNonRegular ) switch ( RedMessage( _LT( "Can't copy the links or special file:\n" ), srcFs->Uri( srcPath ).GetUtf8(), bSkipSkipallCancel ) ) { case CMD_SKIPALL: skipNonRegular = true; // no break case CMD_SKIP: return true; default: return false; } if ( IsSameFile( srcFs, srcPath, &( srcNode->st ), destFs, destPath ) ) { RedMessage( _LT( "Can't copy file to itself:\n" ) , srcFs->Uri( srcPath ).GetUtf8() ); return false; } SendCopyNextFileInfo( srcFs->Uri( srcPath ), destFs->Uri( destPath ) ); SendProgressInfo( srcNode->st.size, 0, 0 ); bool stopped = false; int ret_err; int in = -1; if (destFs->Type() == FS::TYPES::TMP) { // copy and move to tmpfs work the same way. Should we yell on move op instead? FSTmp* destTmpFS = static_cast(destFs); if (!destTmpFS->BaseIsEqual(srcFs)) { RedMessage(_LT("Temporary panel can store only files from the same file system:\n")); return false; } if (srcFs->Type() == FS::TMP && srcNode->IsReg()) { FSString srcFullFSString(srcNode->name.GetUnicode()); FSPath srcFullFSPath(srcFullFSString); //srcFullFSPath.dbg_printf("OperCFThread::CopyFile srcFullFSPath="); //destPath.dbg_printf("OperCFThread::CopyFile destPath="); FSPath destDir(destPath); destDir.Pop(); return destTmpFS->AddNode(srcFullFSPath, destDir); } else return destTmpFS->AddNode(srcPath, srcNode, destPath); } while ( true ) { in = srcFs->OpenRead( srcPath, FS::SHARE_READ, &ret_err, Info() ); if ( in == -2 ) { return false; } if ( in >= 0 ) { break; } switch ( RedMessage( _LT( "Can't open file:\n" ) , srcFs->Uri( srcPath ).GetUtf8(), bRetrySkipCancel, srcFs->StrError( ret_err ).GetUtf8() ) ) { case CMD_CANCEL: return false; case CMD_SKIP: return true; } } int out = -1; out = destFs->OpenCreate( destPath, false | commitAll, srcNode->st.mode, 0, &ret_err, Info() ); if ( out < 0 && destFs->IsEEXIST( ret_err ) ) switch ( RedMessage( _LT( "Overwrite file?\n" ) , destFs->Uri( destPath ).GetUtf8(), bOkAllNoCancel ) ) { case CMD_ALL: commitAll = true; //no break case CMD_OK: out = destFs->OpenCreate( destPath, true, srcNode->st.mode, 0, &ret_err, Info() ); break; case CMD_NO: srcFs->Close( in, 0, Info() ); return true; default: srcFs->Close( in, 0, Info() ); return false; } if ( out < 0 ) { srcFs->Close( in, 0, Info() ); return RedMessage( _LT( "Can't create file:\n" ), destFs->Uri( destPath ).GetUtf8(), bSkipCancel, destFs->StrError( ret_err ).GetUtf8() ) == CMD_SKIP; } int bytes; //char buf[BUFSIZE]; int64_t doneBytes = 0; int blockSize = STARTSIZE; while ( true ) { if ( Info()->Stopped() ) { stopped = true; goto err; } time_t timeStart = time( 0 ); if ( ( bytes = srcFs->Read( in, _buffer, blockSize, &ret_err, Info() ) ) < 0 ) { if ( bytes == -2 || RedMessage( _LT( "Can't read the file:\n" ), srcFs->Uri( srcPath ).GetUtf8(), bSkipCancel, srcFs->StrError( ret_err ).GetUtf8() ) != CMD_SKIP ) { stopped = true; } goto err; } if ( !bytes ) { break; } int b; if ( ( b = destFs->Write( out, _buffer, bytes, &ret_err, Info() ) ) < 0 ) { if ( b == -2 || RedMessage( _LT( "Can't write the file:\n" ), destFs->Uri( destPath ).GetUtf8(), bSkipCancel, destFs->StrError( ret_err ).GetUtf8() ) != CMD_SKIP ) { stopped = true; } goto err; } if ( b != bytes ) { if ( RedMessage( "May be disk full \n(writed bytes != readed bytes)\nwhen write:\n", destFs->Uri( destPath ).GetUtf8(), bSkipCancel ) != CMD_SKIP ) { stopped = true; } goto err; } time_t timeStop = time( 0 ); if ( timeStart == timeStop && blockSize < BSIZE ) { blockSize = blockSize * 2; if ( blockSize > BSIZE ) { blockSize = BSIZE; } } doneBytes += bytes; SendProgressInfo( srcNode->st.size, doneBytes, bytes ); } srcFs->Close( in, 0, Info() ); in = -1; SendProgressInfo( srcNode->st.size, srcNode->st.size, 0 ); { int r = destFs->Close( out, &ret_err, Info() ); if ( r ) { if ( r == -2 || RedMessage( "Can't close the file:\n", destFs->Uri( destPath ).GetUtf8(), bSkipCancel, destFs->StrError( ret_err ).GetUtf8() ) != CMD_SKIP ) { stopped = true; } goto err; } else { out = -1; } } destFs->SetFileTime( destPath, srcNode->st.m_CreationTime, srcNode->st.m_LastWriteTime, srcNode->st.m_LastWriteTime, 0, Info() ); return !move || Unlink( srcFs, srcPath ); err: if ( in >= 0 ) { srcFs->Close( in, 0, Info() ); } if ( out >= 0 ) { destFs->Close( out, 0, Info() ); } Unlink( destFs, destPath ); return !stopped; } bool OperCFThread::CopyDir( FS* srcFs, FSPath& __srcPath, FSNode* srcNode, FS* destFs, FSPath& __destPath, bool move ) { if ( Info()->Stopped() ) { return false; } FSList list; int ret_error; while ( true ) { int ret = srcFs->ReadDir( &list, __srcPath, &ret_error, Info() ); if ( ret == -2 ) { return false; } if ( !ret ) { break; } switch ( RedMessage( _LT( "Can`t open directory:\n" ) , srcFs->Uri( __srcPath ).GetUtf8(), bRetrySkipCancel, srcFs->StrError( ret_error ).GetUtf8() ) ) { case CMD_SKIP: return true; case CMD_RETRY: continue; default: return false; } } while ( destFs->MkDir( __destPath, MkDirMode, &ret_error, Info() ) && !destFs->IsEEXIST( ret_error ) ) { switch ( RedMessage( _LT( "Can't create the directory:\n" ), destFs->Uri( __destPath ).GetUtf8(), bRetrySkipCancel, destFs->StrError( ret_error ).GetUtf8() ) ) { case CMD_CANCEL: return false; case CMD_SKIP: return true; } } FSPath srcPath = __srcPath; int srcPos = srcPath.Count(); FSPath destPath = __destPath; int destPos = destPath.Count(); for ( FSNode* node = list.First(); node; node = node->next ) { if ( Info()->Stopped() ) { return false; } srcPath.SetItemStr( srcPos, node->Name() ); destPath.SetItemStr( destPos, node->Name() ); if ( !CopyNode( srcFs, srcPath, node, destFs, destPath, move ) ) { return false; } } destFs->SetFileTime( destPath, srcNode->st.m_CreationTime, srcNode->st.m_LastWriteTime, srcNode->st.m_LastWriteTime, 0, Info() ); return !move || RmDir( srcFs, __srcPath ); } static void stripPathFromLastItem(FSPath& path) { FSString* lastItem = path.GetItem(path.Count() - 1); if (lastItem) { const unicode_t* lastU = lastItem->GetUnicode(); const unicode_t* lastDelim = unicode_strrchr(lastU, DIR_SPLITTER); if (lastDelim != 0) { path.SetItemStr(path.Count() - 1,FSString(lastDelim + 1)); } } } bool OperCFThread::CopyNode( FS* srcFs, FSPath& srcPath, FSNode* srcNode, FS* destFs, FSPath& destPath, bool move ) { // XXX blame, blame. In tmp panel name has full path. Strip the path from the last item stripPathFromLastItem(destPath); if ( srcNode->st.IsLnk() ) { if ( !CopyLink( srcFs, srcPath, srcNode, destFs, destPath, move ) ) { return false; } } else if ( srcNode->st.IsDir() ) { if ( !CopyDir( srcFs, srcPath, srcNode, destFs, destPath, move ) ) { return false; } } else { if ( !CopyFile( srcFs, srcPath, srcNode, destFs, destPath, move ) ) { return false; } } return true; } bool OperCFThread::Copy( FS* srcFs, FSPath& __srcPath, FSList* list, FS* destFs, FSPath& __destPath, cstrhash& resList ) { if ( list->Count() <= 0 ) { return true; } FSPath srcPath = __srcPath; int srcPos = srcPath.Count(); FSPath destPath = __destPath; int destPos = destPath.Count(); FSStat st; int ret_error; int res = destFs->Stat( __destPath, &st, &ret_error, Info() ); if ( res == -2 ) { return false; } if ( res && !destFs->IsENOENT( ret_error ) ) { RedMessage( _LT( "Can't copy to:\n" ), destFs->Uri( destPath ).GetUtf8(), bOk, destFs->StrError( ret_error ).GetUtf8() ); return false; } bool exist = ( res == 0 ); if ( list->Count() > 1 ) { //если файлов >1 то копировать можно только в каталог if ( !exist ) { RedMessage( _LT( "Can't copy files, destination is not found:\n" ), destFs->Uri( __destPath ).GetUtf8(), bOk ); return false; } if ( !st.IsDir() ) { RedMessage( _LT( "Destination is not directory:\n" ), destFs->Uri( __destPath ).GetUtf8(), bOk ); return false; } for ( FSNode* node = list->First(); node; node = node->next ) { if ( Info()->Stopped() ) { return false; } srcPath.SetItemStr( srcPos, node->Name() ); destPath.SetItemStr(destPos, node->Name() ); if ( !CopyNode( srcFs, srcPath, node, destFs, destPath, false ) ) { return false; } resList[node->Name().GetUnicode()] = true; } } else { // 1 element if ( exist && st.IsDir() ) { destPath.SetItemStr(destPos, list->First()->Name()); } srcPath.SetItemStr( srcPos, list->First()->Name() ); if ( !CopyNode( srcFs, srcPath, list->First(), destFs, destPath, false ) ) { return false; } resList[list->First()->Name().GetUnicode()] = true; }; return true; } void CopyThreadFunc( OperThreadNode* node ) { try { MutexLock lock( node->GetMutex() ); if ( !node->Data() ) { return; } OperCFData* data = ( ( OperCFData* )node->Data() ); OperCFThread thread( "Copy", data->Parent(), node ); clPtr srcFs = data->srcFs; FSPath srcPath = data->srcPath; clPtr destFs = data->destFs; FSPath destPath = data->destPath; clPtr list = data->srcList; lock.Unlock();//!!! try { clPtr > resList = new cstrhash; thread.Copy( srcFs.Ptr(), srcPath, list.ptr(), destFs.Ptr(), destPath, *( resList.ptr() ) ); lock.Lock(); //!!! if ( !node->NBStopped() ) { data->resList = resList; data->executed = true; } } catch ( cexception* ex ) { lock.Lock(); //!!! if ( !node->NBStopped() ) //обязательно надо проверить, иначе 'data' может быть неактуальной { data->errorString = ex->message(); } ex->destroy(); } } catch ( cexception* ex ) { fprintf( stderr, "ERR!!! Error exception in CopyThreadFunc - '%s'\n", ex->message() ); ex->destroy(); } catch ( ... ) { fprintf( stderr, "ERR!!! Unhandled exception in CopyThreadFunc\n" ); } } clPtr > CopyFiles( clPtr srcFs, FSPath& srcPath, clPtr list, clPtr destFs, FSPath& destPath, NCDialogParent* parent ) { CopyDialog dlg( parent ); dlg.threadData.Clear(); dlg.threadData.srcFs = srcFs; dlg.threadData.srcPath = srcPath; dlg.threadData.srcList = list; dlg.threadData.destFs = destFs; dlg.threadData.destPath = destPath; dlg.RunNewThread( "Copy", CopyThreadFunc, &dlg.threadData ); //может быть исключение dlg.Enable(); dlg.Show(); dlg.DoModal(); dlg.StopThread(); return dlg.threadData.resList; } ///////////////////////////////////////////////// MOVE files ///////////////////////////////// /* 0 - ok 1 - need copy -1 - stop */ int OperCFThread::MoveFile( FS* srcFs, FSPath& srcPath, FSNode* srcNode, FS* destFs, FSPath& destPath ) { if ( srcFs != destFs && !srcFs->Equal( destFs ) ) { return 1; } if ( IsSameFile( srcFs, srcPath, &( srcNode->st ), destFs, destPath ) ) { RedMessage( _LT( "Can't move file to itself:\n" ), srcFs->Uri( srcPath ).GetUtf8() ); return -1; } int ret_error; FSStat st; if ( !destFs->Stat( destPath, &st, &ret_error, Info() ) ) { if ( !commitAll ) { switch ( RedMessage( _LT( "Overwrite file?\n" ), destFs->Uri( destPath ).GetUtf8(), bOkAllNoCancel, destFs->StrError( ret_error ).GetUtf8() ) ) { case CMD_ALL: commitAll = true; //no break case CMD_OK: break; case CMD_NO: return 0; default: return -1; } } if ( destFs->Delete( destPath, &ret_error, Info() ) ) return RedMessage( _LT( "Can't delete the file:\n" ), destFs->Uri( destPath ).GetUtf8(), bSkipCancel, destFs->StrError( ret_error ).GetUtf8() ) == CMD_SKIP ? 0 : -1 ; } if ( srcFs->Rename( srcPath, destPath, &ret_error, Info() ) ) { if ( srcFs->IsEXDEV( ret_error ) ) { return 1; } return RedMessage( _LT( "Can't rename the file:\n" ), srcFs->Uri( srcPath ).GetUtf8(), "\nto\n", destFs->Uri( destPath ).GetUtf8(), bSkipCancel, srcFs->StrError( ret_error ).GetUtf8() ) == CMD_SKIP ? 0 : -1; } return 0; } /* 0 - ok 1 - need copy -1 - stop */ int OperCFThread::MoveDir( FS* srcFs, FSPath& __srcPath, FSNode* srcNode, FS* destFs, FSPath& __destPath ) { if ( Info()->Stopped() ) { return -1; } if ( srcFs != destFs ) { return 1; } FSPath srcPath = __srcPath; int srcPos = srcPath.Count(); FSPath destPath = __destPath; int destPos = destPath.Count(); if ( IsSameFile( srcFs, srcPath, &( srcNode->st ), destFs, destPath ) ) { RedMessage( _LT( "Can't move directory to itself:\n" ), srcFs->Uri( __srcPath ).GetUtf8() ); return -1; } FSStat st; int ret_error; if ( !destFs->Stat( destPath, &st, &ret_error, Info() ) ) { if ( !st.IsDir() ) { 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 ) ) { case CMD_CANCEL: return -1; case CMD_SKIP: return 0; } if ( !Unlink( destFs, destPath ) ) { return -1; } } else { FSList list; while ( true ) { int ret = srcFs->ReadDir( &list, srcPath, &ret_error, Info() ); if ( ret == -2 ) { return -1; } if ( !ret ) { break; } switch ( RedMessage( _LT( "Can`t open directory:\n" ), srcFs->Uri( __srcPath ).GetUtf8(), bRetrySkipCancel, srcFs->StrError( ret_error ).GetUtf8() ) ) { case CMD_SKIP: return 0; case CMD_RETRY: continue; default: return -1; } } for ( FSNode* node = list.First(); node; node = node->next ) { if ( Info()->Stopped() ) { return -1; } srcPath.SetItemStr( srcPos, node->Name() ); destPath.SetItemStr( destPos, node->Name() ); if ( !MoveFile( srcFs, srcPath, node, destFs, destPath ) ) { return -1; } } destFs->SetFileTime( destPath, srcNode->st.m_CreationTime, srcNode->st.m_LastWriteTime, srcNode->st.m_LastWriteTime, 0, Info() ); return RmDir( srcFs, srcPath ) ? 0 : -1; } } if ( srcFs->Rename( srcPath, destPath, &ret_error, Info() ) ) { if ( srcFs->IsEXDEV( ret_error ) ) { return 1; } return RedMessage( _LT( "Can't rename the directory:\n" ), srcFs->Uri( srcPath ).GetUtf8(), "\nto\n", destFs->Uri( destPath ).GetUtf8(), bSkipCancel, srcFs->StrError( ret_error ).GetUtf8() ) == CMD_SKIP ? 0 : -1; } return 0; } bool OperCFThread::MoveNode( FS* srcFs, FSPath& srcPath, FSNode* srcNode, FS* destFs, FSPath& destPath ) { if ( srcNode->st.IsLnk() ) { int r = MoveFile( srcFs, srcPath, srcNode, destFs, destPath ); if ( r < 0 ) { return false; } if ( r > 0 && !CopyLink( srcFs, srcPath, srcNode, destFs, destPath, true ) ) { return false; } } else if ( srcNode->st.IsDir() ) { int r = MoveDir( srcFs, srcPath, srcNode, destFs, destPath ); if ( r < 0 ) { return false; } if ( r > 0 && !CopyDir( srcFs, srcPath, srcNode, destFs, destPath, true ) ) { return false; } } else { int r = MoveFile( srcFs, srcPath, srcNode, destFs, destPath ); if ( r < 0 ) { return false; } if ( r > 0 && !CopyFile( srcFs, srcPath, srcNode, destFs, destPath, true ) ) { return false; } } return true; } bool OperCFThread::Move( FS* srcFs, FSPath& __srcPath, FSList* list, FS* destFs, FSPath& __destPath ) { if ( list->Count() <= 0 ) { return true; } FSPath srcPath = __srcPath; int srcPos = srcPath.Count(); FSPath destPath = __destPath; int destPos = destPath.Count(); FSStat st; int ret_error; int r = destFs->Stat( __destPath, &st, &ret_error, Info() ); if ( r == -2 ) { return false; } if ( list->Count() > 1 ) { //если файлов >1 то копировать можно только в каталог if ( r ) { RedMessage( _LT( "Can't move files, bad destination directory:\n" ), destFs->Uri( __destPath ).GetUtf8(), bOk, destFs->StrError( ret_error ).GetUtf8() ); return false; } if ( !st.IsDir() ) { RedMessage( _LT( "Destination is not directory:\n" ), destFs->Uri( __destPath ).GetUtf8(), bOk ); return false; } for ( FSNode* node = list->First(); node; node = node->next ) { srcPath.SetItemStr( srcPos, node->Name() ); destPath.SetItemStr( destPos, node->Name() ); //printf("MOVE '%s'\n", srcPath.GetUtf8()); if ( !MoveNode( srcFs, srcPath, node, destFs, destPath ) ) { return false; } } } else { // 1 element if ( r && !destFs->IsENOENT( ret_error ) ) { RedMessage( _LT( "Can't move to:\n" ), destFs->Uri( destPath ).GetUtf8(), bOk, destFs->StrError( ret_error ).GetUtf8() ); return false; } if ( !r && st.IsDir() ) { destPath.SetItemStr( destPos, list->First()->Name() ); } // FSNode* node = list->First(); srcPath.SetItemStr( srcPos, list->First()->Name() ); if ( !MoveNode( srcFs, srcPath, list->First(), destFs, destPath ) ) { return false; } } return true; } void MoveThreadFunc( OperThreadNode* node ) { try { MutexLock lock( node->GetMutex() ); if ( !node->Data() ) { return; } OperCFData* data = ( ( OperCFData* )node->Data() ); OperCFThread thread( "Copy", data->Parent(), node ); clPtr srcFs = data->srcFs; FSPath srcPath = data->srcPath; clPtr destFs = data->destFs; FSPath destPath = data->destPath; clPtr list = data->srcList; lock.Unlock();//!!! try { thread.Move( srcFs.Ptr(), srcPath, list.ptr(), destFs.Ptr(), destPath ); } catch ( cexception* ex ) { lock.Lock(); //!!! if ( !node->NBStopped() ) //обязательно надо проверить, иначе 'data' может быть неактуальной { data->errorString = ex->message(); } ex->destroy(); } } catch ( cexception* ex ) { fprintf( stderr, "ERR!!! Error exception in MoveThreadFunc - '%s'\n", ex->message() ); ex->destroy(); } catch ( ... ) { fprintf( stderr, "ERR!!! Unhandled exception in MoveThreadFunc\n" ); } } bool MoveFiles( clPtr srcFs, FSPath& srcPath, clPtr list, clPtr destFs, FSPath& destPath, NCDialogParent* parent ) { CopyDialog dlg( parent ); dlg.threadData.Clear(); dlg.threadData.srcFs = srcFs; dlg.threadData.srcPath = srcPath; dlg.threadData.srcList = list; dlg.threadData.destFs = destFs; dlg.threadData.destPath = destPath; dlg.RunNewThread( "Move", MoveThreadFunc, &dlg.threadData ); //может быть исключение dlg.Enable(); dlg.Show(); dlg.DoModal(); dlg.StopThread(); return dlg.threadData.errorString.IsEmpty(); }