From 3b608329b9c66583a0f985c4a4393af5dafc293e Mon Sep 17 00:00:00 2001 From: Dylan Wulf Date: Tue, 10 Nov 2015 12:54:47 +0300 Subject: [PATCH] Added ability to move and rename files/directories --- grive/src/main.cc | 16 +++++++++ libgrive/src/base/Drive.cc | 5 +++ libgrive/src/base/Drive.hh | 1 + libgrive/src/base/State.cc | 63 ++++++++++++++++++++++++++++++++++ libgrive/src/base/State.hh | 3 +- libgrive/src/base/Syncer.hh | 1 + libgrive/src/drive2/Syncer2.cc | 41 ++++++++++++++++++++++ libgrive/src/drive2/Syncer2.hh | 1 + 8 files changed, 130 insertions(+), 1 deletion(-) diff --git a/grive/src/main.cc b/grive/src/main.cc index f0147ba..0472b1a 100644 --- a/grive/src/main.cc +++ b/grive/src/main.cc @@ -121,6 +121,7 @@ int Main( int argc, char **argv ) ( "dry-run", "Only detect which files need to be uploaded/downloaded, " "without actually performing them." ) ( "ignore", po::value(), "Ignore files relative paths of which match this Perl RegExp." ) + ( "move,m", po::value >()->multitoken(), "Syncs, then moves a file (first argument) to new location (second argument) without reuploading or redownloading." ) ; po::variables_map vm; @@ -203,6 +204,21 @@ int Main( int argc, char **argv ) } else drive.DryRun() ; + + if ( vm.count ( "move" ) > 0 && vm.count( "dry-run" ) == 0 ) + { + if (vm["move"].as >().size() < 2 ) + Log( "Not enough arguments for move. Move failed.", log::error ); + else + { + bool success = drive.Move( vm["move"].as >()[0], + vm["move"].as >()[1] ); + if (success) + Log( "Move successful!", log::info ); + else + Log( "Move failed.", log::error); + } + } config.Save() ; Log( "Finished!", log::info ) ; diff --git a/libgrive/src/base/Drive.cc b/libgrive/src/base/Drive.cc index 7aa282a..63128a1 100644 --- a/libgrive/src/base/Drive.cc +++ b/libgrive/src/base/Drive.cc @@ -150,6 +150,11 @@ void Drive::DetectChanges() } } +bool Drive::Move( fs::path old_p, fs::path new_p ) +{ + return m_state.Move( m_syncer, old_p, new_p, m_options["path"].Str() ); +} + void Drive::Update() { Log( "Synchronizing files", log::info ) ; diff --git a/libgrive/src/base/Drive.hh b/libgrive/src/base/Drive.hh index 7a63999..db1a027 100644 --- a/libgrive/src/base/Drive.hh +++ b/libgrive/src/base/Drive.hh @@ -41,6 +41,7 @@ public : Drive( Syncer *syncer, const Val& options ) ; void DetectChanges() ; + bool Move( fs::path old_p, fs::path new_p ); void Update() ; void DryRun() ; void SaveState() ; diff --git a/libgrive/src/base/State.cc b/libgrive/src/base/State.cc index fed5f15..bec157e 100644 --- a/libgrive/src/base/State.cc +++ b/libgrive/src/base/State.cc @@ -321,4 +321,67 @@ void State::ChangeStamp( long cstamp ) m_cstamp = cstamp ; } +bool State::Move( Syncer* syncer, fs::path old_p, fs::path new_p, fs::path grive_root ) +{ + //Convert paths to canonical representations + //Also seems to remove trailing / at the end of directory paths + old_p = fs::canonical( old_p ); + grive_root = fs::canonical( grive_root ); + + //new_p is a little special because fs::canonical() requires that the path exists + if ( new_p.string()[ new_p.string().size() - 1 ] == '/') //If new_p ends with a /, remove it + new_p = new_p.parent_path(); + new_p = fs::canonical( new_p.parent_path() ) / new_p.filename(); + + //Fails if source file doesn't exist, or if destination file already + //exists and is not a directory, or if the source and destination are exactly the same + if ( (fs::exists(new_p) && !fs::is_directory(new_p) ) || !fs::exists(old_p) || fs::equivalent( old_p, new_p ) ) + return false; + + //If new path is an existing directory, move the file into the directory + //instead of trying to rename it + if ( fs::is_directory(new_p) ){ + new_p = new_p / old_p.filename(); + } + + //Get the paths relative to grive root. + //Just finds the substring from the end of the grive_root to the end of the path + //+1s are to exclude slash at beginning of relative path + int start = grive_root.string().size() + 1; + int nLen = new_p.string().size() - (grive_root.string().size() + 1); + int oLen = old_p.string().size() - (grive_root.string().size() + 1); + if ( start + nLen != new_p.string().size() || start + oLen != old_p.string().size() ) + return false; + fs::path new_p_rootrel( new_p.string().substr( start, nLen ) ); + fs::path old_p_rootrel( old_p.string().substr( start, oLen ) ); + + //Get resources + Resource* res = m_res.Root(); + Resource* newParentRes = m_res.Root(); + for ( fs::path::iterator it = old_p_rootrel.begin(); it != old_p_rootrel.end(); ++it ) + { + if ( *it != "." && *it != ".." && res != 0 ) + res = res->FindChild(it->string()); + if ( *it == ".." ) + res = res->Parent(); + } + for ( fs::path::iterator it = new_p_rootrel.begin(); it != new_p_rootrel.end(); ++it ) + { + if ( *it != "." && *it != ".." && *it != new_p.filename() && newParentRes != 0 ) + newParentRes = newParentRes->FindChild(it->string()); + if ( *it == "..") + res = res->Parent(); + } + + //These conditions should only occur if everything is not up-to-date + if ( res == 0 || newParentRes == 0 || res->GetState() != Resource::sync || + newParentRes->GetState() != Resource::sync || + newParentRes->FindChild( new_p.filename().string() ) != 0 ) + return false; + + fs::rename(old_p, new_p); //Moves local file + syncer->Move(res, newParentRes, new_p.filename().string()); //Moves server file + return true; +} + } // end of namespace gr diff --git a/libgrive/src/base/State.hh b/libgrive/src/base/State.hh index 53814eb..7822be9 100644 --- a/libgrive/src/base/State.hh +++ b/libgrive/src/base/State.hh @@ -63,7 +63,8 @@ public : long ChangeStamp() const ; void ChangeStamp( long cstamp ) ; - + bool Move( Syncer* syncer, fs::path old_p, fs::path new_p, fs::path grive_root ); + private : void FromLocal( const fs::path& p, Resource *folder ) ; void FromChange( const Entry& e ) ; diff --git a/libgrive/src/base/Syncer.hh b/libgrive/src/base/Syncer.hh index 33375ee..19adacb 100644 --- a/libgrive/src/base/Syncer.hh +++ b/libgrive/src/base/Syncer.hh @@ -53,6 +53,7 @@ public : virtual void Download( Resource *res, const fs::path& file ); virtual bool EditContent( Resource *res, bool new_rev ) = 0; virtual bool Create( Resource *res ) = 0; + virtual bool Move( Resource* res, Resource* newParent, std::string newFilename ) = 0; virtual std::auto_ptr GetFolders() = 0; virtual std::auto_ptr GetAll() = 0; diff --git a/libgrive/src/drive2/Syncer2.cc b/libgrive/src/drive2/Syncer2.cc index f8fc890..4c0f290 100644 --- a/libgrive/src/drive2/Syncer2.cc +++ b/libgrive/src/drive2/Syncer2.cc @@ -89,6 +89,47 @@ bool Syncer2::Create( Resource *res ) return Upload( res ); } +bool Syncer2::Move( Resource* res, Resource* newParentRes, std::string newFilename ) +{ + if ( res->ResourceID().empty() ) + { + Log("Can't rename file %1%, no server id found", res->Name()); + return false; + } + + Val meta; + meta.Add( "title", Val(newFilename) ); + if ( res->IsFolder() ) + { + meta.Add( "mimeType", Val( mime_types::folder ) ); + } + std::string json_meta = WriteJson( meta ); + + Val valr ; + + // Issue metadata update request + { + std::string addRemoveParents(""); + if (res->Parent()->IsRoot() ) + addRemoveParents += "&removeParents=root"; + else + addRemoveParents += "&removeParents=" + res->Parent()->ResourceID(); + if ( newParentRes->IsRoot() ) + addRemoveParents += "&addParents=root"; + else + addRemoveParents += "&addParents=" + newParentRes->ResourceID(); + http::Header hdr2 ; + hdr2.Add( "Content-Type: application/json" ); + http::ValResponse vrsp ; + long http_code = 0; + //Don't change modified date because we're only moving + http_code = m_http->Put( feeds::files + "/" + res->ResourceID() + "?modifiedDateBehavior=noChange" + addRemoveParents, json_meta, &vrsp, hdr2 ) ; + valr = vrsp.Response(); + assert( !( valr["id"].Str().empty() ) ); + } + return true; +} + std::string to_string( uint64_t n ) { std::ostringstream s; diff --git a/libgrive/src/drive2/Syncer2.hh b/libgrive/src/drive2/Syncer2.hh index 0ab6286..96f93fc 100644 --- a/libgrive/src/drive2/Syncer2.hh +++ b/libgrive/src/drive2/Syncer2.hh @@ -37,6 +37,7 @@ public : void DeleteRemote( Resource *res ); bool EditContent( Resource *res, bool new_rev ); bool Create( Resource *res ); + bool Move( Resource* res, Resource* newParent, std::string newFilename ); std::auto_ptr GetFolders(); std::auto_ptr GetAll();