mirror of https://github.com/vitalif/grive2
Merge branch 'master' of https://github.com/match065/grive
commit
b0dd9a53db
|
@ -40,8 +40,7 @@ Config::Config() :
|
|||
|
||||
void Config::Save( )
|
||||
{
|
||||
StdioFile file( Filename(), "w" ) ;
|
||||
file.Chmod( 0600 ) ;
|
||||
StdioFile file( Filename(), 0600 ) ;
|
||||
m_cfg.Write( file ) ;
|
||||
}
|
||||
|
||||
|
|
|
@ -121,7 +121,7 @@ int main( int argc, char **argv )
|
|||
{
|
||||
OAuth2 token( refresh_token, client_id, client_secret ) ;
|
||||
Drive drive( token ) ;
|
||||
|
||||
|
||||
drive.Update() ;
|
||||
drive.SaveState() ;
|
||||
|
||||
|
|
|
@ -93,22 +93,27 @@ Drive::Drive( OAuth2& auth ) :
|
|||
if ( (*i)["content"] == "" )
|
||||
continue ;
|
||||
|
||||
Entry file( *i ) ;
|
||||
if ( file.Kind() != "folder" )
|
||||
Entry entry( *i ) ;
|
||||
if ( entry.Kind() != "folder" )
|
||||
{
|
||||
Resource *p = m_state.FindFolderByHref( file.ParentHref() ) ;
|
||||
if ( file.Filename().empty() )
|
||||
Log( "file \"%1%\" is a google document, ignored", file.Title(), log::info ) ;
|
||||
Resource *parent = m_state.FindByHref( entry.ParentHref() ) ;
|
||||
std::string fn = entry.Filename() ;
|
||||
|
||||
else if ( file.ParentHref().empty() || p == 0 || !p->IsInRootTree() )
|
||||
Log( "file \"%1%\" parent doesn't exist, ignored", file.Title(), log::info ) ;
|
||||
if ( fn.empty() )
|
||||
Log( "file \"%1%\" is a google document, ignored", entry.Title(), log::verbose ) ;
|
||||
|
||||
else if ( p != 0 && !p->IsFolder() )
|
||||
Log( "entry %1% has parent %2% which is not a folder, ignored",
|
||||
file.Title(), p->Name(), log::info ) ;
|
||||
else if ( fn.find('/') != fn.npos )
|
||||
Log( "file \"%1%\" contains a slash in its name, ignored", entry.Title(), log::verbose ) ;
|
||||
|
||||
else if ( parent == 0 || !parent->IsInRootTree() )
|
||||
Log( "file \"%1%\" parent doesn't exist, ignored", entry.Title(), log::verbose ) ;
|
||||
|
||||
else if ( parent != 0 && !parent->IsFolder() )
|
||||
Log( "warning: entry %1% has parent %2% which is not a folder, ignored",
|
||||
entry.Title(), parent->Name(), log::warning ) ;
|
||||
|
||||
else
|
||||
m_state.FromRemote( file ) ;
|
||||
m_state.FromRemote( entry ) ;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,7 +136,7 @@ void Drive::SaveState()
|
|||
void Drive::ConstructDirTree( http::Agent *http )
|
||||
{
|
||||
http::XmlResponse xml ;
|
||||
http->Get( feed_base + "/-/folder?max-results=10&showroot=true", &xml, m_http_hdr ) ;
|
||||
http->Get( feed_base + "/-/folder?max-results=50&showroot=true", &xml, m_http_hdr ) ;
|
||||
|
||||
xml::Node resp = xml.Response() ;
|
||||
|
||||
|
@ -145,11 +150,14 @@ void Drive::ConstructDirTree( http::Agent *http )
|
|||
Entry e( *i ) ;
|
||||
if ( e.Kind() == "folder" )
|
||||
{
|
||||
if ( e.ParentHrefs().size() == 1 )
|
||||
m_state.FromRemote( e ) ;
|
||||
|
||||
else
|
||||
if ( e.ParentHrefs().size() != 1 )
|
||||
Log( "folder \"%1%\" has multiple parents, ignored", e.Title(), log::warning ) ;
|
||||
|
||||
else if ( e.Title().find('/') != std::string::npos )
|
||||
Log( "folder \"%1%\" contains a slash in its name, ignored", e.Title(), log::verbose ) ;
|
||||
|
||||
else
|
||||
m_state.FromRemote( e ) ;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ public :
|
|||
|
||||
private :
|
||||
void ConstructDirTree( http::Agent *http ) ;
|
||||
void file();
|
||||
|
||||
private :
|
||||
OAuth2& m_auth ;
|
||||
|
|
|
@ -39,6 +39,16 @@
|
|||
|
||||
namespace gr {
|
||||
|
||||
// hard coded XML file
|
||||
const std::string xml_meta =
|
||||
"<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
"<entry xmlns=\"http://www.w3.org/2005/Atom\" xmlns:docs=\"http://schemas.google.com/docs/2007\">"
|
||||
"<category scheme=\"http://schemas.google.com/g/2005#kind\" "
|
||||
"term=\"http://schemas.google.com/docs/2007#%1%\"/>"
|
||||
"<title>%2%</title>"
|
||||
"</entry>" ;
|
||||
|
||||
|
||||
/// default constructor creates the root folder
|
||||
Resource::Resource() :
|
||||
m_parent( 0 ),
|
||||
|
@ -93,12 +103,20 @@ Resource::Resource( const fs::path& path ) :
|
|||
/// one is newer.
|
||||
void Resource::FromRemote( const Entry& remote )
|
||||
{
|
||||
// sync folder is easy
|
||||
if ( remote.Kind() == "folder" && IsFolder() )
|
||||
{
|
||||
Log( "folder %1% is in sync", Name(), log::verbose ) ;
|
||||
m_state = sync ;
|
||||
}
|
||||
|
||||
// if checksum is equal, no need to compare the mtime
|
||||
if ( remote.MD5() == m_entry.MD5() )
|
||||
else if ( remote.MD5() == m_entry.MD5() )
|
||||
{
|
||||
Log( "MD5 matches: %1% is already in sync", Name(), log::verbose ) ;
|
||||
m_state = sync ;
|
||||
}
|
||||
|
||||
// use mtime to check which one is more recent
|
||||
else
|
||||
{
|
||||
|
@ -116,24 +134,34 @@ void Resource::FromRemote( const Entry& remote )
|
|||
void Resource::FromLocal()
|
||||
{
|
||||
fs::path path = Path() ;
|
||||
if ( !fs::exists( path ) )
|
||||
|
||||
// root folder is always rsync
|
||||
if ( m_parent == 0 )
|
||||
m_state = sync ;
|
||||
|
||||
else if ( !fs::exists( path ) )
|
||||
{
|
||||
m_state = local_deleted ;
|
||||
Log( "%1% in state but not exist on disk: %2%", Name(), m_state ) ;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
m_state = local_new ;
|
||||
|
||||
// to save time, compare mtime before checksum
|
||||
DateTime mtime = os::FileMTime( path ) ;
|
||||
if ( mtime > m_entry.MTime() )
|
||||
|
||||
// no need to compare MD5 or mtime for directories
|
||||
if ( !IsFolder() )
|
||||
{
|
||||
Log( "%1% mtime newer on disk: %2%", Name(), m_state ) ;
|
||||
m_entry.Update( crypt::MD5( path ), mtime ) ;
|
||||
// to save time, compare mtime before checksum
|
||||
DateTime mtime = os::FileMTime( path ) ;
|
||||
if ( mtime > m_entry.MTime() )
|
||||
{
|
||||
Log( "%1% mtime newer on disk: %2%", Name(), m_state ) ;
|
||||
m_entry.Update( crypt::MD5( path ), mtime ) ;
|
||||
}
|
||||
else
|
||||
Log( "%1% unchanged on disk: %2%", Name(), m_state ) ;
|
||||
}
|
||||
else
|
||||
Log( "%1% unchanged on disk: %2%", Name(), m_state ) ;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -154,11 +182,13 @@ std::string Resource::ResourceID() const
|
|||
|
||||
const Resource* Resource::Parent() const
|
||||
{
|
||||
assert( m_parent == 0 || m_parent->IsFolder() ) ;
|
||||
return m_parent ;
|
||||
}
|
||||
|
||||
Resource* Resource::Parent()
|
||||
{
|
||||
assert( m_parent == 0 || m_parent->IsFolder() ) ;
|
||||
return m_parent ;
|
||||
}
|
||||
|
||||
|
@ -182,6 +212,7 @@ void Resource::Swap( Resource& coll )
|
|||
m_entry.Swap( coll.m_entry ) ;
|
||||
std::swap( m_parent, coll.m_parent ) ;
|
||||
m_child.swap( coll.m_child ) ;
|
||||
std::swap( m_state, coll.m_state ) ;
|
||||
}
|
||||
|
||||
bool Resource::IsFolder() const
|
||||
|
@ -217,17 +248,19 @@ Resource* Resource::FindChild( const std::string& name )
|
|||
// try to change the state to "sync"
|
||||
void Resource::Sync( http::Agent *http, const http::Headers& auth )
|
||||
{
|
||||
// no need to update for folders
|
||||
if ( IsFolder() )
|
||||
// root folder is already synced
|
||||
if ( IsRoot() )
|
||||
{
|
||||
m_state = sync ;
|
||||
return ;
|
||||
|
||||
assert( m_parent != 0 ) ;
|
||||
}
|
||||
|
||||
switch ( m_state )
|
||||
{
|
||||
case local_new :
|
||||
Log( "sync %1% %2% doesn't exist in server. upload \"%3%\"?",
|
||||
m_entry.Title(), m_entry.Filename(), m_parent->m_entry.CreateLink(), log::verbose ) ;
|
||||
Log( "sync %1% doesn't exist in server. upload \"%2%\"?",
|
||||
Name(), m_parent->m_entry.CreateLink(), log::verbose ) ;
|
||||
|
||||
if ( Create( http, auth ) )
|
||||
m_state = sync ;
|
||||
break ;
|
||||
|
@ -279,6 +312,12 @@ void Resource::Download( http::Agent* http, const fs::path& file, const http::He
|
|||
|
||||
bool Resource::EditContent( http::Agent* http, const http::Headers& auth )
|
||||
{
|
||||
assert( m_parent != 0 ) ;
|
||||
|
||||
// sync parent first. make sure the parent folder exists in remote
|
||||
if ( m_parent->m_state != sync )
|
||||
m_parent->Sync( http, auth ) ;
|
||||
|
||||
// upload link missing means that file is read only
|
||||
if ( m_entry.EditLink().empty() )
|
||||
{
|
||||
|
@ -292,23 +331,46 @@ bool Resource::EditContent( http::Agent* http, const http::Headers& auth )
|
|||
bool Resource::Create( http::Agent* http, const http::Headers& auth )
|
||||
{
|
||||
assert( m_parent != 0 ) ;
|
||||
return Upload( http, m_parent->m_entry.CreateLink() + "?convert=false", auth, true ) ;
|
||||
|
||||
// sync parent first. make sure the parent folder exists in remote
|
||||
if ( m_parent->m_state != sync )
|
||||
m_parent->Sync( http, auth ) ;
|
||||
|
||||
if ( IsFolder() )
|
||||
{
|
||||
std::string uri = feed_base ;
|
||||
if ( !m_parent->IsRoot() )
|
||||
uri += ("/folder%3A" + m_parent->ResourceID() ) ;
|
||||
|
||||
std::string meta = (boost::format(xml_meta) % "folder" % Name() ).str() ;
|
||||
|
||||
http::Headers hdr( auth ) ;
|
||||
hdr.push_back( "Content-Type: application/atom+xml" ) ;
|
||||
|
||||
http::XmlResponse xml ;
|
||||
http->Post( uri, meta, &xml, hdr ) ;
|
||||
m_entry.Update( xml.Response() ) ;
|
||||
|
||||
return true ;
|
||||
}
|
||||
else if ( !m_parent->m_entry.CreateLink().empty() )
|
||||
{
|
||||
return Upload( http, m_parent->m_entry.CreateLink() + "?convert=false", auth, true ) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log( "parent of %1% does not exist: cannot upload", Name() ) ;
|
||||
return false ;
|
||||
}
|
||||
}
|
||||
|
||||
bool Resource::Upload( http::Agent* http, const std::string& link, const http::Headers& auth, bool post )
|
||||
{
|
||||
Log( "Uploading %1%", m_entry.Title() ) ;
|
||||
|
||||
std::string meta =
|
||||
"<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
"<entry xmlns=\"http://www.w3.org/2005/Atom\" xmlns:docs=\"http://schemas.google.com/docs/2007\">"
|
||||
"<category scheme=\"http://schemas.google.com/g/2005#kind\" "
|
||||
"term=\"http://schemas.google.com/docs/2007#file\"/>"
|
||||
"<title>" + m_entry.Filename() + "</title>"
|
||||
"</entry>" ;
|
||||
|
||||
StdioFile file( Path(), "rb" ) ;
|
||||
StdioFile file( Path() ) ;
|
||||
|
||||
// TODO: upload in chunks
|
||||
std::string data ;
|
||||
char buf[4096] ;
|
||||
std::size_t count = 0 ;
|
||||
|
@ -325,6 +387,8 @@ bool Resource::Upload( http::Agent* http, const std::string& link, const http::H
|
|||
hdr.push_back( "If-Match: " + m_entry.ETag() ) ;
|
||||
hdr.push_back( "Expect:" ) ;
|
||||
|
||||
std::string meta = (boost::format( xml_meta ) % m_entry.Kind() % Name()).str() ;
|
||||
|
||||
http::StringResponse str ;
|
||||
if ( post )
|
||||
http->Post( link, meta, &str, hdr ) ;
|
||||
|
@ -395,6 +459,18 @@ std::ostream& operator<<( std::ostream& os, Resource::State s )
|
|||
return os << state[s] ;
|
||||
}
|
||||
|
||||
std::string Resource::StateStr() const
|
||||
{
|
||||
std::ostringstream ss ;
|
||||
ss << m_state ;
|
||||
return ss.str() ;
|
||||
}
|
||||
|
||||
bool Resource::IsRoot() const
|
||||
{
|
||||
return m_parent == 0 ;
|
||||
}
|
||||
|
||||
} // end of namespace
|
||||
|
||||
namespace std
|
||||
|
|
|
@ -69,6 +69,7 @@ public :
|
|||
|
||||
fs::path Path() const ;
|
||||
bool IsInRootTree() const ;
|
||||
bool IsRoot() const ;
|
||||
|
||||
void FromRemote( const Entry& e ) ;
|
||||
void FromLocal() ;
|
||||
|
@ -83,6 +84,8 @@ public :
|
|||
iterator end() const ;
|
||||
std::size_t size() const ;
|
||||
|
||||
std::string StateStr() const ;
|
||||
|
||||
private :
|
||||
/// State of the resource. indicating what to do with the resource
|
||||
enum State
|
||||
|
|
|
@ -94,6 +94,8 @@ ResourceTree& ResourceTree::operator=( const ResourceTree& fs )
|
|||
|
||||
Resource* ResourceTree::FindByHref( const std::string& href )
|
||||
{
|
||||
// for the resource that not yet have href (e.g. not yet fetched from server)
|
||||
// their href will be empty.
|
||||
if ( href.empty() )
|
||||
return 0 ;
|
||||
|
||||
|
@ -113,9 +115,23 @@ const Resource* ResourceTree::FindByHref( const std::string& href ) const
|
|||
/// container. It traverses the tree instead.
|
||||
Resource* ResourceTree::FindByPath( const fs::path& path )
|
||||
{
|
||||
// not yet implemented
|
||||
assert( false ) ;
|
||||
return false ;
|
||||
Resource *current = m_root ;
|
||||
for ( fs::path::iterator i = path.begin() ; i != path.end() && current != 0 ; ++i )
|
||||
{
|
||||
Trace( "path it = %1%", *i ) ;
|
||||
|
||||
// current directory
|
||||
if ( *i == "." )
|
||||
continue ;
|
||||
|
||||
else if ( *i == ".." )
|
||||
current = current->Parent() ;
|
||||
|
||||
else
|
||||
current = current->FindChild( i->filename().string() ) ;
|
||||
}
|
||||
|
||||
return current ;
|
||||
}
|
||||
|
||||
/// Reinsert should be called when the ID/HREF were updated
|
||||
|
@ -165,6 +181,8 @@ ResourceTree::iterator ResourceTree::end()
|
|||
void ResourceTree::Read( const Json& json )
|
||||
{
|
||||
Clear() ;
|
||||
|
||||
assert( m_root == 0 ) ;
|
||||
m_root = new Resource ;
|
||||
AddTree( m_root, json ) ;
|
||||
}
|
||||
|
|
|
@ -64,6 +64,9 @@ void State::FromLocal( const fs::path& p, gr::Resource* folder )
|
|||
{
|
||||
assert( folder != 0 ) ;
|
||||
assert( folder->IsFolder() ) ;
|
||||
|
||||
// sync the folder itself
|
||||
folder->FromLocal() ;
|
||||
|
||||
for ( fs::directory_iterator i( p ) ; i != fs::directory_iterator() ; ++i )
|
||||
{
|
||||
|
@ -176,11 +179,17 @@ bool State::Update( const Entry& e )
|
|||
return false ;
|
||||
}
|
||||
|
||||
Resource* State::FindFolderByHref( const std::string& href )
|
||||
Resource* State::FindByHref( const std::string& href )
|
||||
{
|
||||
return m_res.FindByHref( href ) ;
|
||||
}
|
||||
|
||||
Resource* State::Find( const fs::path& path )
|
||||
{
|
||||
return m_res.FindByPath( path ) ;
|
||||
}
|
||||
|
||||
|
||||
State::iterator State::begin()
|
||||
{
|
||||
return m_res.begin() ;
|
||||
|
|
|
@ -48,12 +48,13 @@ public :
|
|||
std::string ChangeStamp() const ;
|
||||
void ChangeStamp( const std::string& cs ) ;
|
||||
|
||||
Resource* FindFolderByHref( const std::string& href ) ;
|
||||
Resource* FindFolderByID( const std::string& id ) ;
|
||||
Resource* FindByHref( const std::string& href ) ;
|
||||
Resource* FindByID( const std::string& id ) ;
|
||||
Resource* Find( const fs::path& path ) ;
|
||||
|
||||
iterator begin() ;
|
||||
iterator end() ;
|
||||
|
||||
|
||||
private :
|
||||
void FromLocal( const fs::path& p, Resource *folder ) ;
|
||||
bool Update( const Entry& e ) ;
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
namespace gr { namespace http {
|
||||
|
||||
Download::Download( const std::string& filename ) :
|
||||
m_file( filename, "wb" ),
|
||||
m_file( filename, 0600 ),
|
||||
m_mdctx( ::EVP_MD_CTX_create() )
|
||||
{
|
||||
if ( m_mdctx == 0 )
|
||||
|
@ -53,7 +53,7 @@ Download::Download( const std::string& filename ) :
|
|||
}
|
||||
|
||||
Download::Download( const std::string& filename, NoChecksum ) :
|
||||
m_file( filename, "wb" ),
|
||||
m_file( filename, 0600 ),
|
||||
m_mdctx( 0 )
|
||||
{
|
||||
}
|
||||
|
|
|
@ -38,6 +38,14 @@ Json::Json( ) :
|
|||
BOOST_THROW_EXCEPTION( Error() << expt::ErrMsg( "cannot create json object" ) ) ;
|
||||
}
|
||||
|
||||
Json::Json( const char *str ) :
|
||||
m_json( ::json_object_new_string( str ) )
|
||||
{
|
||||
if ( m_json == 0 )
|
||||
BOOST_THROW_EXCEPTION(
|
||||
Error() << expt::ErrMsg( "cannot create json string \"" + std::string(str) + "\"" ) ) ;
|
||||
}
|
||||
|
||||
template <>
|
||||
Json::Json( const std::string& str ) :
|
||||
m_json( ::json_object_new_string( str.c_str() ) )
|
||||
|
@ -96,17 +104,17 @@ Json Json::Parse( const std::string& str )
|
|||
|
||||
Json Json::ParseFile( const std::string& filename )
|
||||
{
|
||||
StdioFile file( filename, "r" ) ;
|
||||
StdioFile file( filename ) ;
|
||||
struct json_tokener *tok = ::json_tokener_new() ;
|
||||
|
||||
struct json_object *json = 0 ;
|
||||
|
||||
char buf[80] ;
|
||||
char buf[1024] ;
|
||||
std::size_t count = 0 ;
|
||||
|
||||
while ( (count = file.Read( buf, sizeof(buf) ) ) > 0 )
|
||||
json = ::json_tokener_parse_ex( tok, buf, count ) ;
|
||||
|
||||
|
||||
if ( json == 0 )
|
||||
BOOST_THROW_EXCEPTION( Error() << expt::ErrMsg( ::json_tokener_errors[tok->err] ) ) ;
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ public :
|
|||
|
||||
Json() ;
|
||||
Json( const Json& rhs ) ;
|
||||
Json( const char *str ) ;
|
||||
~Json( ) ;
|
||||
|
||||
static Json Parse( const std::string& str ) ;
|
||||
|
|
|
@ -29,11 +29,13 @@
|
|||
|
||||
namespace gr { namespace crypt {
|
||||
|
||||
const std::size_t read_size = 8 * 1024 ;
|
||||
|
||||
std::string MD5( const fs::path& file )
|
||||
{
|
||||
try
|
||||
{
|
||||
StdioFile sfile( file, "rb" ) ;
|
||||
StdioFile sfile( file ) ;
|
||||
return MD5( sfile ) ;
|
||||
}
|
||||
catch ( StdioFile::Error& )
|
||||
|
@ -44,16 +46,14 @@ std::string MD5( const fs::path& file )
|
|||
|
||||
std::string MD5( StdioFile& file )
|
||||
{
|
||||
char buf[64 * 1024] ;
|
||||
char buf[read_size] ;
|
||||
EVP_MD_CTX md ;
|
||||
EVP_MD_CTX_init( &md );
|
||||
EVP_DigestInit_ex( &md, EVP_md5(), 0 ) ;
|
||||
|
||||
std::size_t count = 0 ;
|
||||
while ( (count = file.Read( buf, sizeof(buf) )) > 0 )
|
||||
{
|
||||
EVP_DigestUpdate( &md, buf, count ) ;
|
||||
}
|
||||
|
||||
unsigned int md5_size = EVP_MAX_MD_SIZE ;
|
||||
unsigned char md5[EVP_MAX_MD_SIZE] ;
|
||||
|
|
|
@ -31,17 +31,23 @@
|
|||
#include <boost/exception/info.hpp>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
namespace gr {
|
||||
|
||||
StdioFile::StdioFile( const std::string& filename, const char *mode ) : m_file( 0 )
|
||||
StdioFile::StdioFile( ) : m_fd( -1 )
|
||||
{
|
||||
Open( filename, mode ) ;
|
||||
}
|
||||
|
||||
StdioFile::StdioFile( const fs::path& path, const char *mode ) : m_file( 0 )
|
||||
StdioFile::StdioFile( const fs::path& path ) : m_fd( -1 )
|
||||
{
|
||||
Open( path, mode ) ;
|
||||
OpenForRead( path ) ;
|
||||
}
|
||||
|
||||
StdioFile::StdioFile( const fs::path& path, int mode ) : m_fd( -1 )
|
||||
{
|
||||
OpenForWrite( path, mode ) ;
|
||||
}
|
||||
|
||||
StdioFile::~StdioFile( )
|
||||
|
@ -49,84 +55,95 @@ StdioFile::~StdioFile( )
|
|||
Close() ;
|
||||
}
|
||||
|
||||
void StdioFile::Open( const std::string& filename, const char *mode )
|
||||
void StdioFile::Open( const fs::path& path, int flags, int mode )
|
||||
{
|
||||
if ( IsOpened() )
|
||||
Close() ;
|
||||
|
||||
assert( m_file == 0 ) ;
|
||||
m_file = std::fopen( filename.c_str(), mode ) ;
|
||||
|
||||
if ( m_file == 0 )
|
||||
assert( m_fd == -1 ) ;
|
||||
m_fd = ::open( path.string().c_str(), flags, mode ) ;
|
||||
if ( m_fd == -1 )
|
||||
{
|
||||
BOOST_THROW_EXCEPTION(
|
||||
Error()
|
||||
<< boost::errinfo_api_function("fopen")
|
||||
<< boost::errinfo_api_function("open")
|
||||
<< boost::errinfo_errno(errno)
|
||||
<< boost::errinfo_file_name(filename)
|
||||
<< boost::errinfo_file_open_mode(mode)
|
||||
<< boost::errinfo_file_name(path.string())
|
||||
) ;
|
||||
}
|
||||
}
|
||||
|
||||
void StdioFile::Open( const fs::path& path, const char *mode )
|
||||
void StdioFile::OpenForRead( const fs::path& path )
|
||||
{
|
||||
Open( path.string(), mode ) ;
|
||||
Open( path, O_RDONLY, 0 ) ;
|
||||
}
|
||||
|
||||
void StdioFile::OpenForWrite( const fs::path& path, int mode )
|
||||
{
|
||||
Open( path, O_CREAT|O_RDWR|O_TRUNC, mode ) ;
|
||||
}
|
||||
|
||||
void StdioFile::Close()
|
||||
{
|
||||
if ( IsOpened() )
|
||||
{
|
||||
std::fclose( m_file ) ;
|
||||
m_file = 0 ;
|
||||
close( m_fd ) ;
|
||||
m_fd = -1 ;
|
||||
}
|
||||
}
|
||||
|
||||
bool StdioFile::IsOpened() const
|
||||
{
|
||||
return m_file != 0 ;
|
||||
return m_fd != -1 ;
|
||||
}
|
||||
|
||||
std::size_t StdioFile::Read( void *ptr, std::size_t size )
|
||||
{
|
||||
assert( m_file != 0 ) ;
|
||||
return std::fread( ptr, 1, size, m_file ) ;
|
||||
assert( IsOpened() ) ;
|
||||
ssize_t count = ::read( m_fd, ptr, size ) ;
|
||||
if ( count == -1 )
|
||||
{
|
||||
BOOST_THROW_EXCEPTION(
|
||||
Error()
|
||||
<< boost::errinfo_api_function("read")
|
||||
<< boost::errinfo_errno(errno)
|
||||
) ;
|
||||
}
|
||||
return count ;
|
||||
}
|
||||
|
||||
std::size_t StdioFile::Write( const void *ptr, std::size_t size )
|
||||
{
|
||||
assert( m_file != 0 ) ;
|
||||
return std::fwrite( ptr, 1, size, m_file ) ;
|
||||
assert( IsOpened() ) ;
|
||||
ssize_t count = ::write( m_fd, ptr, size ) ;
|
||||
if ( count == -1 )
|
||||
{
|
||||
BOOST_THROW_EXCEPTION(
|
||||
Error()
|
||||
<< boost::errinfo_api_function("read")
|
||||
<< boost::errinfo_errno(errno)
|
||||
) ;
|
||||
}
|
||||
return count ;
|
||||
}
|
||||
|
||||
int StdioFile::Seek( long offset, int whence )
|
||||
long StdioFile::Seek( long offset, int whence )
|
||||
{
|
||||
assert( m_file != 0 ) ;
|
||||
return std::fseek( m_file, offset, whence ) ;
|
||||
assert( IsOpened() ) ;
|
||||
return ::lseek( m_fd, offset, whence ) ;
|
||||
}
|
||||
|
||||
long StdioFile::Tell() const
|
||||
{
|
||||
assert( m_file != 0 ) ;
|
||||
return std::ftell( m_file ) ;
|
||||
assert( IsOpened() ) ;
|
||||
return ::lseek( m_fd, 0, SEEK_CUR ) ;
|
||||
}
|
||||
|
||||
void StdioFile::Chmod( int mode )
|
||||
{
|
||||
assert( m_file != 0 ) ;
|
||||
assert( IsOpened() ) ;
|
||||
|
||||
int fd = ::fileno(m_file) ;
|
||||
if ( fd == -1 )
|
||||
{
|
||||
BOOST_THROW_EXCEPTION(
|
||||
Error()
|
||||
<< boost::errinfo_api_function("fileno")
|
||||
<< boost::errinfo_errno(errno)
|
||||
) ;
|
||||
}
|
||||
|
||||
if ( ::fchmod( fd, mode ) != 0 )
|
||||
if ( ::fchmod( m_fd, mode ) != 0 )
|
||||
{
|
||||
BOOST_THROW_EXCEPTION(
|
||||
Error()
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include "Exception.hh"
|
||||
#include "FileSystem.hh"
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
namespace gr {
|
||||
|
@ -33,25 +32,29 @@ public :
|
|||
struct Error : virtual Exception {} ;
|
||||
|
||||
public :
|
||||
StdioFile( const std::string& filename, const char *mode ) ;
|
||||
StdioFile( const fs::path& path, const char *mode ) ;
|
||||
StdioFile() ;
|
||||
StdioFile( const fs::path& path ) ;
|
||||
StdioFile( const fs::path& path, int mode ) ;
|
||||
~StdioFile( ) ;
|
||||
|
||||
void Open( const std::string& filename, const char *mode ) ;
|
||||
void Open( const fs::path& path, const char* mode ) ;
|
||||
void OpenForRead( const fs::path& path ) ;
|
||||
void OpenForWrite( const fs::path& path, int mode = 0600 ) ;
|
||||
void Close() ;
|
||||
bool IsOpened() const ;
|
||||
|
||||
std::size_t Read( void *ptr, std::size_t size ) ;
|
||||
std::size_t Write( const void *ptr, std::size_t size ) ;
|
||||
|
||||
int Seek( long offset, int whence ) ;
|
||||
long Seek( long offset, int whence ) ;
|
||||
long Tell() const ;
|
||||
|
||||
void Chmod( int mode ) ;
|
||||
|
||||
private :
|
||||
void Open( const fs::path& path, int flags, int mode ) ;
|
||||
|
||||
private :
|
||||
std::FILE *m_file ;
|
||||
int m_fd ;
|
||||
} ;
|
||||
|
||||
} // end of namespace
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "util/DefaultLog.hh"
|
||||
|
||||
#include "drive/EntryTest.hh"
|
||||
#include "drive/ResourceTest.hh"
|
||||
#include "drive/ResourceTreeTest.hh"
|
||||
#include "drive/StateTest.hh"
|
||||
#include "util/DateTimeTest.hh"
|
||||
|
@ -40,6 +41,7 @@ int main( int argc, char **argv )
|
|||
CppUnit::TextUi::TestRunner runner;
|
||||
runner.addTest( EntryTest::suite( ) ) ;
|
||||
runner.addTest( StateTest::suite( ) ) ;
|
||||
runner.addTest( ResourceTest::suite( ) ) ;
|
||||
runner.addTest( ResourceTreeTest::suite( ) ) ;
|
||||
runner.addTest( DateTimeTest::suite( ) ) ;
|
||||
runner.addTest( FunctionTest::suite( ) ) ;
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{ "change_stamp": "", "rtree": { "name": ".", "id": "folder:root", "href": "https:\/\/docs.google.com\/feeds\/default\/private\/full\/folder%3Aroot", "md5": "", "kind": "folder", "mtime": { "sec": 0, "nsec": 0 }, "child": [ { "name": "entry.xml", "id": "", "href": "", "md5": "c0742c0a32b2c909b6f176d17a6992d0", "kind": "file", "mtime": { "sec": 1336796872, "nsec": 404985662 }, "child": [ ] } ] } }
|
|
@ -0,0 +1 @@
|
|||
abc
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
grive: an GPL program to sync a local directory with Google Drive
|
||||
Copyright (C) 2012 Wan Wai Ho
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation version 2
|
||||
of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ResourceTest.hh"
|
||||
|
||||
#include "Assert.hh"
|
||||
|
||||
#include "drive/Resource.hh"
|
||||
#include "protocol/Json.hh"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace grut {
|
||||
|
||||
using namespace gr ;
|
||||
|
||||
ResourceTest::ResourceTest( )
|
||||
{
|
||||
}
|
||||
|
||||
void ResourceTest::TestNormal( )
|
||||
{
|
||||
Json json ;
|
||||
json.Add( "name", Json( "abc.txt" ) ) ;
|
||||
|
||||
Resource subject( json, 0 ) ;
|
||||
}
|
||||
|
||||
} // end of namespace grut
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
grive: an GPL program to sync a local directory with Google Drive
|
||||
Copyright (C) 2012 Wan Wai Ho
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation version 2
|
||||
of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
|
||||
namespace grut {
|
||||
|
||||
class ResourceTest : public CppUnit::TestFixture
|
||||
{
|
||||
public :
|
||||
ResourceTest( ) ;
|
||||
|
||||
// declare suit function
|
||||
CPPUNIT_TEST_SUITE( ResourceTest ) ;
|
||||
CPPUNIT_TEST( TestNormal ) ;
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
private :
|
||||
void TestNormal( ) ;
|
||||
} ;
|
||||
|
||||
} // end of namespace
|
|
@ -22,6 +22,7 @@
|
|||
#include "Assert.hh"
|
||||
|
||||
#include "drive/State.hh"
|
||||
#include "util/Log.hh"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
@ -35,9 +36,16 @@ StateTest::StateTest( )
|
|||
|
||||
void StateTest::TestSync( )
|
||||
{
|
||||
State s( ".grive_state" ) ;
|
||||
s.FromLocal( TEST_DATA ) ;
|
||||
s.Write( "" ) ;
|
||||
State s( TEST_DATA "/test_dir1.state" ) ;
|
||||
Resource *r = s.Find( "./folder1/abc.txt" ) ;
|
||||
CPPUNIT_ASSERT( r != 0 ) ;
|
||||
GRUT_ASSERT_EQUAL( r->Name(), "abc.txt" ) ;
|
||||
|
||||
Trace( "state %1% = %2%", r->Name(), r->StateStr() ) ;
|
||||
|
||||
// load directory
|
||||
s.FromLocal( TEST_DATA "/test_dir1" ) ;
|
||||
|
||||
}
|
||||
|
||||
} // end of namespace grut
|
||||
|
|
Loading…
Reference in New Issue