From ceca83dc72f9b6d6e8647c707d1602ff1f99e7e9 Mon Sep 17 00:00:00 2001 From: Matchman Green Date: Sat, 28 Apr 2012 01:52:02 +0800 Subject: [PATCH] set the mtime of the local file to be the same as the remote --- CMakeLists.txt | 2 +- src/drive/Drive.cc | 13 ++++++++-- src/protocol/Json.cc | 6 ++++- src/protocol/Json.hh | 8 ++++++ src/util/DateTime.cc | 51 +++++++++++++++++++++++++++++++-------- src/util/DateTime.hh | 9 +++++-- src/util/OS.cc | 20 +++++++++++++++ src/util/OS.hh | 3 ++- test/util/DateTimeTest.cc | 23 ++++++++++++++++++ test/util/DateTimeTest.hh | 6 +++++ 10 files changed, 124 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d9f5963..e345418 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ add_executable( grive src/protocol/HTTP.cc src/protocol/Json.cc src/protocol/OAuth2.cc - src/util/DateTime.hh + src/util/DateTime.cc src/util/OS.cc ) diff --git a/src/drive/Drive.cc b/src/drive/Drive.cc index 06267ca..91641c1 100644 --- a/src/drive/Drive.cc +++ b/src/drive/Drive.cc @@ -22,6 +22,8 @@ #include "protocol/HTTP.hh" #include "protocol/Json.hh" #include "protocol/OAuth2.hh" +#include "util/DateTime.hh" +#include "util/OS.hh" // dependent libraries #include @@ -180,10 +182,12 @@ void Drive::UpdateFile( const Json& entry ) if ( entry.Has( "docs$filename" ) ) { // use title as the filename - std::string filename = entry["docs$filename"]["$t"].As() ; - std::string url = entry["content"]["src"].As() ; + std::string filename = entry["docs$filename"]["$t"].As() ; + std::string url = entry["content"]["src"].As() ; std::string parent_href = Parent( entry ) ; + DateTime remote( entry["updated"]["$t"].As() ) ; + bool changed = true ; std::string path = "./" + filename ; @@ -194,11 +198,16 @@ void Drive::UpdateFile( const Json& entry ) if ( pit != m_coll.end() ) path = pit->Path() + "/" + filename ; } + DateTime local = os::FileMTime( path ) ; + + std::cout << "file time: " << entry["updated"]["$t"].As() << " " << remote << " " << local << std::endl ; // compare checksum first if file exists std::ifstream ifile( path.c_str(), std::ios::binary | std::ios::out ) ; if ( ifile && entry.Has("docs$md5Checksum") ) { + os::SetFileTime( path, remote ) ; + std::string remote_md5 = entry["docs$md5Checksum"]["$t"].As() ; std::string local_md5 = MD5( ifile.rdbuf() ) ; diff --git a/src/protocol/Json.cc b/src/protocol/Json.cc index 381cfe8..0fcc12a 100644 --- a/src/protocol/Json.cc +++ b/src/protocol/Json.cc @@ -138,6 +138,11 @@ void Json::Add( const std::string& key, const Json& json ) ::json_object_object_add( m_json, key.c_str(), json.m_json ) ; } +Json::Proxy Json::As() const +{ + return Proxy( *this ) ; +} + template <> std::string Json::As() const { @@ -243,5 +248,4 @@ bool Json::FindInArray( const std::string& key, const std::string& value, Json& } } - } diff --git a/src/protocol/Json.hh b/src/protocol/Json.hh index c977d0e..5417526 100644 --- a/src/protocol/Json.hh +++ b/src/protocol/Json.hh @@ -52,6 +52,14 @@ public : template T As() const ; + struct Proxy + { + const Json& referring ; + explicit Proxy( const Json& j ) : referring( j ) { } + template operator T() const { return referring.As() ; } + } ; + Proxy As() const ; + template bool Is() const ; diff --git a/src/util/DateTime.cc b/src/util/DateTime.cc index b396885..b32111b 100644 --- a/src/util/DateTime.cc +++ b/src/util/DateTime.cc @@ -20,7 +20,9 @@ #include "DateTime.hh" #include +#include #include +#include #include @@ -28,20 +30,32 @@ namespace gr { DateTime::DateTime( ) : m_sec ( 0 ), - m_usec ( 0 ) + m_nsec ( 0 ) { } DateTime::DateTime( const std::string& iso ) : m_sec ( 0 ), - m_usec ( 0 ) + m_nsec ( 0 ) +{ + struct tm tp = {} ; + const char *r = ::strptime( iso.c_str(), "%Y-%m-%dT%H:%M:%S", &tp ) ; + + // should be '.' followed by 3 digits and 'Z' (e.g. .123Z) + if ( r != 0 && r - iso.c_str() == 19 ) + { + m_sec = ::timegm( &tp ) ; + + // at least 3 digits is OK. we don't care the Z + if ( *r == '.' && ::strlen( r+1 ) >= 3 ) + m_nsec = std::atoi( r+1 ) * 1000 * 1000 ; + } +} + +DateTime::DateTime( std::time_t sec, unsigned long nsec ) : + m_sec ( sec ), + m_nsec ( nsec ) { - struct tm tp ; - const char *r = ::strptime( iso.c_str(), "%Y-%m-%dT%H:%M:%S.", &tp ) ; - - m_sec = ::mktime( &tp ) ; - if ( r != 0 ) - m_usec = std::atoi( r ) * 1000 ; } struct tm DateTime::Tm() const @@ -56,9 +70,26 @@ std::time_t DateTime::Sec( ) const return m_sec ; } -unsigned long DateTime::USec( ) const +unsigned long DateTime::NanoSec( ) const { - return m_usec ; + return m_nsec ; +} + +std::ostream& operator<<( std::ostream& os, const DateTime& dt ) +{ + struct tm tp = dt.Tm() ; + + char buf[40] ; + strftime( buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &tp ) ; + return os << buf << '.' << std::setw( 3 ) << std::setfill('0') << dt.NanoSec()/1000000 << 'Z' ; +} + +struct timeval DateTime::Tv() const +{ + timeval result ; + result.tv_sec = m_sec ; + result.tv_usec = m_nsec / 1000 ; + return result ; } } // end of namespace diff --git a/src/util/DateTime.hh b/src/util/DateTime.hh index 97a1764..a2e1ae4 100644 --- a/src/util/DateTime.hh +++ b/src/util/DateTime.hh @@ -21,6 +21,7 @@ #include #include +#include namespace gr { @@ -29,15 +30,19 @@ class DateTime public : DateTime( ) ; explicit DateTime( const std::string& iso ) ; + explicit DateTime( std::time_t sec, unsigned long nsec = 0 ) ; std::time_t Sec( ) const ; - unsigned long USec( ) const ; + unsigned long NanoSec( ) const ; struct tm Tm() const ; + struct timeval Tv() const ; private : std::time_t m_sec ; - unsigned long m_usec ; + unsigned long m_nsec ; } ; +std::ostream& operator<<( std::ostream& os, const DateTime& dt ) ; + } // end of namespace diff --git a/src/util/OS.cc b/src/util/OS.cc index a69dd2f..a70ce0a 100644 --- a/src/util/OS.cc +++ b/src/util/OS.cc @@ -18,9 +18,13 @@ */ #include "OS.hh" +#include "DateTime.hh" + +#include // OS specific headers #include +#include #include namespace gr { namespace os { @@ -30,4 +34,20 @@ void MakeDir( const std::string& dir ) mkdir( dir.c_str(), 0700 ) ; } +DateTime FileMTime( const std::string& filename ) +{ + struct stat s = {} ; + if ( ::stat( filename.c_str(), &s ) != 0 ) + throw std::runtime_error( "cannot get file attribute of " + filename ) ; + + return DateTime( s.st_mtim.tv_sec, s.st_mtim.tv_nsec ) ; +} + +void SetFileTime( const std::string& filename, const DateTime& t ) +{ + struct timeval tvp[2] = { t.Tv(), t.Tv() } ; + if ( ::utimes( filename.c_str(), tvp ) != 0 ) + throw std::runtime_error( "cannot set file time" ) ; +} + } } // end of namespaces diff --git a/src/util/OS.hh b/src/util/OS.hh index a108357..2f52e98 100644 --- a/src/util/OS.hh +++ b/src/util/OS.hh @@ -28,7 +28,8 @@ class DateTime ; namespace os { void MakeDir( const std::string& dir ) ; - DateTime FileMTime( const std::string& file ) ; + DateTime FileMTime( const std::string& filename ) ; + void SetFileTime( const std::string& filename, const DateTime& t ) ; } } // end of namespaces diff --git a/test/util/DateTimeTest.cc b/test/util/DateTimeTest.cc index 88c4820..6c803f4 100644 --- a/test/util/DateTimeTest.cc +++ b/test/util/DateTimeTest.cc @@ -21,6 +21,8 @@ #include "util/DateTime.hh" +#include + namespace grut { using namespace gr ; @@ -35,6 +37,27 @@ void DateTimeTest::TestParseIso( ) struct tm tp = subject.Tm() ; CPPUNIT_ASSERT( tp.tm_year == 109 ) ; CPPUNIT_ASSERT( tp.tm_sec == 39 ) ; + CPPUNIT_ASSERT_EQUAL( 804000000UL, subject.NanoSec() ) ; +} + +void DateTimeTest::TestParseNoMillisec( ) +{ + DateTime subject( "2009-07-29T20:31:39Z" ) ; + CPPUNIT_ASSERT_EQUAL( 0UL, subject.NanoSec() ) ; +} + +void DateTimeTest::TestParseInvalid( ) +{ + DateTime subject( "abcdefg" ) ; + CPPUNIT_ASSERT_EQUAL( static_cast(0), subject.Sec() ) ; + CPPUNIT_ASSERT_EQUAL( 0UL, subject.NanoSec() ) ; +} + +void DateTimeTest::TestOffByOne( ) +{ + DateTime subject( "2008-12-21T02:48:53.940Z" ) ; + struct tm tp = subject.Tm() ; + CPPUNIT_ASSERT_EQUAL( 21, tp.tm_mday ) ; } } // end of namespace grut diff --git a/test/util/DateTimeTest.hh b/test/util/DateTimeTest.hh index 79cf9af..1246a33 100644 --- a/test/util/DateTimeTest.hh +++ b/test/util/DateTimeTest.hh @@ -32,10 +32,16 @@ public : // declare suit function CPPUNIT_TEST_SUITE( DateTimeTest ) ; CPPUNIT_TEST( TestParseIso ) ; + CPPUNIT_TEST( TestParseNoMillisec ) ; + CPPUNIT_TEST( TestOffByOne ) ; + CPPUNIT_TEST( TestParseInvalid ) ; CPPUNIT_TEST_SUITE_END(); private : void TestParseIso( ) ; + void TestParseNoMillisec( ) ; + void TestOffByOne( ) ; + void TestParseInvalid( ) ; } ; } // end of namespace