Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge remote-tracking branch 'upstream/master'

  • Loading branch information...
commit c12029a5cc92851939f0ec95068026504043920b 2 parents d5315f4 + c6a1c47
justin authored
View
5 grive/src/main.cc
@@ -21,6 +21,8 @@
#include "drive/Drive.hh"
+#include "http/CurlAgent.hh"
+#include "protocol/AuthAgent.hh"
#include "protocol/OAuth2.hh"
#include "protocol/Json.hh"
@@ -172,7 +174,8 @@ int Main( int argc, char **argv )
}
OAuth2 token( refresh_token, client_id, client_secret ) ;
- Drive drive( token, options ) ;
+ AuthAgent agent( token, std::auto_ptr<http::Agent>( new http::CurlAgent ) ) ;
+ Drive drive( &agent, options ) ;
drive.DetectChanges() ;
if ( vm.count( "dry-run" ) == 0 )
View
2  libgrive/CMakeLists.txt
@@ -29,6 +29,8 @@ endif ( BFD_FOUND )
if ( IBERTY_FOUND )
set( OPT_LIBS ${OPT_LIBS} ${IBERTY_LIBRARY} )
+else ( IBERTY_FOUND )
+ set( IBERTY_LIBRARY "" )
endif ( IBERTY_FOUND )
if ( ZLIB_FOUND )
View
42 libgrive/src/drive/Drive.cc
@@ -23,11 +23,11 @@
#include "Entry.hh"
#include "Feed.hh"
-#include "http/CurlAgent.hh"
+#include "http/Agent.hh"
#include "http/ResponseLog.hh"
#include "http/XmlResponse.hh"
#include "protocol/Json.hh"
-#include "protocol/OAuth2.hh"
+// #include "protocol/OAuth2.hh"
#include "util/Destroy.hh"
#include "util/log/Log.hh"
#include "xml/Node.hh"
@@ -53,12 +53,11 @@ namespace
const std::string state_file = ".grive_state" ;
}
-Drive::Drive( OAuth2& auth, const Json& options ) :
- m_auth( auth ),
+Drive::Drive( http::Agent *http, const Json& options ) :
+ m_http( http ),
m_state( state_file, options )
{
- m_http_hdr.Add( "Authorization: Bearer " + m_auth.AccessToken() ) ;
- m_http_hdr.Add( "GData-Version: 3.0" ) ;
+ assert( m_http != 0 ) ;
}
void Drive::FromRemote( const Entry& entry )
@@ -93,14 +92,14 @@ void Drive::SaveState()
m_state.Write( state_file ) ;
}
-void Drive::SyncFolders( http::Agent *http )
+void Drive::SyncFolders( )
{
- assert( http != 0 ) ;
+ assert( m_http != 0 ) ;
Log( "Synchronizing folders", log::info ) ;
http::XmlResponse xml ;
- http->Get( feed_base + "/-/folder?max-results=50&showroot=true", &xml, m_http_hdr ) ;
+ m_http->Get( feed_base + "/-/folder?max-results=50&showroot=true", &xml, http::Header() ) ;
Feed feed( xml.Response() ) ;
do
@@ -121,7 +120,7 @@ void Drive::SyncFolders( http::Agent *http )
m_state.FromRemote( e ) ;
}
}
- } while ( feed.GetNext( http, m_http_hdr ) ) ;
+ } while ( feed.GetNext( m_http, http::Header() ) ) ;
m_state.ResolveEntry() ;
}
@@ -131,17 +130,15 @@ void Drive::DetectChanges()
Log( "Reading local directories", log::info ) ;
m_state.FromLocal( "." ) ;
- http::CurlAgent http ;
-
long prev_stamp = m_state.ChangeStamp() ;
Trace( "previous change stamp is %1%", prev_stamp ) ;
- SyncFolders( &http ) ;
+ SyncFolders( ) ;
Log( "Reading remote server file list", log::info ) ;
Feed feed ;
// feed.EnableLog( "/tmp/file", ".xml" ) ;
- feed.Start( &http, m_http_hdr, feed_base + "?showfolders=true&showroot=true" ) ;
+ feed.Start( m_http, http::Header(), feed_base + "?showfolders=true&showroot=true" ) ;
m_resume_link = feed.Root()["link"].
Find( "@rel", "http://schemas.google.com/g/2005#resumable-create-media" )["@href"] ;
@@ -152,7 +149,7 @@ void Drive::DetectChanges()
feed.begin(), feed.end(),
boost::bind( &Drive::FromRemote, this, _1 ) ) ;
- } while ( feed.GetNext( &http, m_http_hdr ) ) ;
+ } while ( feed.GetNext( m_http, http::Header() ) ) ;
// pull the changes feed
if ( prev_stamp != -1 )
@@ -160,7 +157,7 @@ void Drive::DetectChanges()
Log( "Detecting changes from last sync", log::info ) ;
Feed changes ;
// feed.EnableLog( "/tmp/changes", ".xml" ) ;
- feed.Start( &http, m_http_hdr, ChangesFeed(prev_stamp+1) ) ;
+ feed.Start( m_http, http::Header(), ChangesFeed(prev_stamp+1) ) ;
std::for_each(
changes.begin(), changes.end(),
@@ -171,25 +168,24 @@ void Drive::DetectChanges()
void Drive::Update()
{
Log( "Synchronizing files", log::info ) ;
- http::CurlAgent http ;
- m_state.Sync( &http, m_http_hdr ) ;
+ m_state.Sync( m_http ) ;
- UpdateChangeStamp( &http ) ;
+ UpdateChangeStamp( ) ;
}
void Drive::DryRun()
{
Log( "Synchronizing files (dry-run)", log::info ) ;
- m_state.Sync( 0, m_http_hdr ) ;
+ m_state.Sync( 0 ) ;
}
-void Drive::UpdateChangeStamp( http::Agent *http )
+void Drive::UpdateChangeStamp( )
{
- assert( http != 0 ) ;
+ assert( m_http != 0 ) ;
// get changed feed
http::XmlResponse xrsp ;
- http->Get( ChangesFeed(m_state.ChangeStamp()+1), &xrsp, m_http_hdr ) ;
+ m_http->Get( ChangesFeed(m_state.ChangeStamp()+1), &xrsp, http::Header() ) ;
// we should go through the changes to see if it was really Grive to made that change
// maybe by recording the updated timestamp and compare it?
View
12 libgrive/src/drive/Drive.hh
@@ -35,13 +35,12 @@ namespace http
}
class Entry ;
-class OAuth2 ;
class Json ;
class Drive
{
public :
- Drive( OAuth2& auth, const Json& options ) ;
+ Drive( http::Agent *http, const Json& options ) ;
void DetectChanges() ;
void Update() ;
@@ -51,15 +50,16 @@ public :
struct Error : virtual Exception {} ;
private :
- void SyncFolders( http::Agent *http ) ;
+ void SyncFolders( ) ;
void file();
void FromRemote( const Entry& entry ) ;
void FromChange( const Entry& entry ) ;
- void UpdateChangeStamp( http::Agent *http ) ;
+ void UpdateChangeStamp( ) ;
private :
- OAuth2& m_auth ;
- http::Header m_http_hdr ;
+ http::Agent *m_http ;
+// OAuth2& m_auth ;
+// http::Header m_http_hdr ;
std::string m_resume_link ;
State m_state ;
View
39 libgrive/src/drive/Resource.cc
@@ -365,20 +365,20 @@ Resource* Resource::FindChild( const std::string& name )
}
// try to change the state to "sync"
-void Resource::Sync( http::Agent *http, const http::Header& auth, DateTime& sync_time )
+void Resource::Sync( http::Agent *http, DateTime& sync_time )
{
assert( m_state != unknown ) ;
assert( !IsRoot() || m_state == sync ) ; // root folder is already synced
- SyncSelf( http, auth, sync_time ) ;
+ SyncSelf( http, sync_time ) ;
// if myself is deleted, no need to do the childrens
if ( m_state != local_deleted && m_state != remote_deleted )
std::for_each( m_child.begin(), m_child.end(),
- boost::bind( &Resource::Sync, _1, http, auth, boost::ref(sync_time) ) ) ;
+ boost::bind( &Resource::Sync, _1, http, boost::ref(sync_time) ) ) ;
}
-void Resource::SyncSelf( http::Agent* http, const http::Header& auth, DateTime& sync_time )
+void Resource::SyncSelf( http::Agent* http, DateTime& sync_time )
{
assert( !IsRoot() || m_state == sync ) ; // root is always sync
assert( IsRoot() || http == 0 || fs::is_directory( m_parent->Path() ) ) ;
@@ -392,19 +392,19 @@ void Resource::SyncSelf( http::Agent* http, const http::Header& auth, DateTime&
case local_new :
Log( "sync %1% doesn't exist in server, uploading", path, log::info ) ;
- if ( http != 0 && Create( http, auth, sync_time ) )
+ if ( http != 0 && Create( http, sync_time ) )
m_state = sync ;
break ;
case local_deleted :
Log( "sync %1% deleted in local. deleting remote", path, log::info ) ;
if ( http != 0 )
- DeleteRemote( http, auth ) ;
+ DeleteRemote( http ) ;
break ;
case local_changed :
Log( "sync %1% changed in local. uploading", path, log::info ) ;
- if ( http != 0 && EditContent( http, auth, sync_time ) )
+ if ( http != 0 && EditContent( http, sync_time ) )
m_state = sync ;
break ;
@@ -415,7 +415,7 @@ void Resource::SyncSelf( http::Agent* http, const http::Header& auth, DateTime&
if ( IsFolder() )
fs::create_directories( path ) ;
else
- Download( http, path, auth ) ;
+ Download( http, path ) ;
m_state = sync ;
}
@@ -426,7 +426,7 @@ void Resource::SyncSelf( http::Agent* http, const http::Header& auth, DateTime&
Log( "sync %1% changed in remote. downloading", path, log::info ) ;
if ( http != 0 )
{
- Download( http, path, auth ) ;
+ Download( http, path ) ;
m_state = sync ;
}
break ;
@@ -474,14 +474,14 @@ void Resource::DeleteLocal()
}
}
-void Resource::DeleteRemote( http::Agent *http, const http::Header& auth )
+void Resource::DeleteRemote( http::Agent *http )
{
assert( http != 0 ) ;
http::StringResponse str ;
try
{
- http::Header hdr( auth ) ;
+ http::Header hdr ;
hdr.Add( "If-Match: " + m_etag ) ;
// doesn't know why, but an update before deleting seems to work always
@@ -502,12 +502,12 @@ void Resource::DeleteRemote( http::Agent *http, const http::Header& auth )
}
-void Resource::Download( http::Agent* http, const fs::path& file, const http::Header& auth ) const
+void Resource::Download( http::Agent* http, const fs::path& file ) const
{
assert( http != 0 ) ;
http::Download dl( file.string(), http::Download::NoChecksum() ) ;
- long r = http->Get( m_content, &dl, auth ) ;
+ long r = http->Get( m_content, &dl, http::Header() ) ;
if ( r <= 400 )
{
if ( m_mtime != DateTime() )
@@ -517,7 +517,7 @@ void Resource::Download( http::Agent* http, const fs::path& file, const http::He
}
}
-bool Resource::EditContent( http::Agent* http, const http::Header& auth, DateTime& sync_time )
+bool Resource::EditContent( http::Agent* http, DateTime& sync_time )
{
assert( http != 0 ) ;
assert( m_parent != 0 ) ;
@@ -530,10 +530,10 @@ bool Resource::EditContent( http::Agent* http, const http::Header& auth, DateTim
return false ;
}
- return Upload( http, m_edit, auth, false, sync_time ) ;
+ return Upload( http, m_edit, false, sync_time ) ;
}
-bool Resource::Create( http::Agent* http, const http::Header& auth, DateTime& sync_time )
+bool Resource::Create( http::Agent* http, DateTime& sync_time )
{
assert( http != 0 ) ;
assert( m_parent != 0 ) ;
@@ -551,7 +551,7 @@ bool Resource::Create( http::Agent* http, const http::Header& auth, DateTime& sy
% xml::Escape(m_name)
).str() ;
- http::Header hdr( auth ) ;
+ http::Header hdr ;
hdr.Add( "Content-Type: application/atom+xml" ) ;
http::XmlResponse xml ;
@@ -563,7 +563,7 @@ bool Resource::Create( http::Agent* http, const http::Header& auth, DateTime& sy
}
else if ( !m_parent->m_create.empty() )
{
- return Upload( http, m_parent->m_create + "?convert=false", auth, true, sync_time ) ;
+ return Upload( http, m_parent->m_create + "?convert=false", true, sync_time ) ;
}
else
{
@@ -575,7 +575,6 @@ bool Resource::Create( http::Agent* http, const http::Header& auth, DateTime& sy
bool Resource::Upload(
http::Agent* http,
const std::string& link,
- const http::Header& auth,
bool post,
DateTime& sync_time )
{
@@ -585,7 +584,7 @@ bool Resource::Upload(
std::ostringstream xcontent_len ;
xcontent_len << "X-Upload-Content-Length: " << file.Size() ;
- http::Header hdr( auth ) ;
+ http::Header hdr ;
hdr.Add( "Content-Type: application/atom+xml" ) ;
hdr.Add( "X-Upload-Content-Type: application/octet-stream" ) ;
hdr.Add( xcontent_len.str() ) ;
View
15 libgrive/src/drive/Resource.hh
@@ -32,7 +32,6 @@ namespace gr {
namespace http
{
class Agent ;
- class Header ;
}
class Entry ;
@@ -77,7 +76,7 @@ public :
void FromRemote( const Entry& remote, const DateTime& last_sync ) ;
void FromLocal( const DateTime& last_sync ) ;
- void Sync( http::Agent* http, const http::Header& auth, DateTime& sync_time ) ;
+ void Sync( http::Agent* http, DateTime& sync_time ) ;
// children access
iterator begin() const ;
@@ -124,19 +123,19 @@ private :
private :
void SetState( State new_state ) ;
- void Download( http::Agent* http, const fs::path& file, const http::Header& auth ) const ;
- bool EditContent( http::Agent* http, const http::Header& auth, DateTime& sync_time ) ;
- bool Create( http::Agent* http, const http::Header& auth, DateTime& sync_time ) ;
- bool Upload( http::Agent* http, const std::string& link, const http::Header& auth, bool post, DateTime& sync_time ) ;
+ void Download( http::Agent* http, const fs::path& file ) const ;
+ bool EditContent( http::Agent* http, DateTime& sync_time ) ;
+ bool Create( http::Agent* http, DateTime& sync_time ) ;
+ bool Upload( http::Agent* http, const std::string& link, bool post, DateTime& sync_time ) ;
void FromRemoteFolder( const Entry& remote, const DateTime& last_sync ) ;
void FromRemoteFile( const Entry& remote, const DateTime& last_sync ) ;
void DeleteLocal() ;
- void DeleteRemote( http::Agent* http, const http::Header& auth ) ;
+ void DeleteRemote( http::Agent* http ) ;
void AssignIDs( const Entry& remote ) ;
- void SyncSelf( http::Agent* http, const http::Header& auth, DateTime& sync_time ) ;
+ void SyncSelf( http::Agent* http, DateTime& sync_time ) ;
private :
std::string m_name ;
View
14 libgrive/src/drive/State.cc
@@ -24,7 +24,6 @@
#include "CommonUri.hh"
#include "http/Agent.hh"
-#include "http/Header.hh"
#include "util/Crypt.hh"
#include "util/log/Log.hh"
#include "protocol/Json.hh"
@@ -262,7 +261,7 @@ void State::Write( const fs::path& filename ) const
fs << result ;
}
-void State::Sync( http::Agent *http, const http::Header& auth )
+void State::Sync( http::Agent *http )
{
// set the last sync time from the time returned by the server for the last file synced
// if the sync time hasn't changed (i.e. now files have been uploaded)
@@ -272,10 +271,15 @@ void State::Sync( http::Agent *http, const http::Header& auth )
// TODO - WARNING - do we use the last sync time to compare to client file times
// need to check if this introduces a new problem
DateTime last_sync_time = m_last_sync;
- m_res.Root()->Sync( http, auth, last_sync_time ) ;
- if (last_sync_time == m_last_sync) {
+ m_res.Root()->Sync( http, last_sync_time ) ;
+ if ( last_sync_time == m_last_sync )
+ {
+ Trace( "nothing changed? %1%", m_last_sync ) ;
m_last_sync = DateTime::Now();
- } else {
+ }
+ else
+ {
+ Trace( "updating last sync? %1%", last_sync_time ) ;
m_last_sync = last_sync_time;
}
}
View
3  libgrive/src/drive/State.hh
@@ -31,7 +31,6 @@ namespace gr {
namespace http
{
class Agent ;
- class Header ;
}
class Json ;
@@ -58,7 +57,7 @@ public :
Resource* FindByID( const std::string& id ) ;
Resource* Find( const fs::path& path ) ;
- void Sync( http::Agent *http, const http::Header& auth ) ;
+ void Sync( http::Agent *http ) ;
iterator begin() ;
iterator end() ;
View
2  libgrive/src/http/CurlAgent.cc
@@ -156,7 +156,7 @@ long CurlAgent::ExecCurl(
Trace( "HTTP response %1%", http_code ) ;
::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, 0 ) ;
- if ( curl_code != CURLE_OK || http_code >= 400 )
+ if ( curl_code != CURLE_OK || (http_code >= 400 && http_code < 500) )
{
BOOST_THROW_EXCEPTION(
Error()
View
7 libgrive/src/http/Header.cc
@@ -50,4 +50,11 @@ std::ostream& operator<<( std::ostream& os, const Header& h )
return os ;
}
+Header operator+( const Header& header, const std::string& str )
+{
+ Header h( header ) ;
+ h.Add( str ) ;
+ return h ;
+}
+
} } // end of namespace
View
1  libgrive/src/http/Header.hh
@@ -46,5 +46,6 @@ private :
} ;
std::ostream& operator<<( std::ostream& os, const Header& h ) ;
+Header operator+( const Header& header, const std::string& str ) ;
}} // end of namespace
View
140 libgrive/src/protocol/AuthAgent.cc
@@ -0,0 +1,140 @@
+/*
+ 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 "AuthAgent.hh"
+
+#include "http/Header.hh"
+#include "util/log/Log.hh"
+#include "util/OS.hh"
+
+#include <cassert>
+
+namespace gr {
+
+using namespace http ;
+
+AuthAgent::AuthAgent( const OAuth2& auth, std::auto_ptr<Agent> real_agent ) :
+ m_auth ( auth ),
+ m_agent ( real_agent )
+{
+ assert( m_agent.get() != 0 ) ;
+}
+
+Header AuthAgent::AppendHeader( const Header& hdr ) const
+{
+ Header h(hdr) ;
+ h.Add( "Authorization: Bearer " + m_auth.AccessToken() ) ;
+ h.Add( "GData-Version: 3.0" ) ;
+ return h ;
+}
+
+long AuthAgent::Put(
+ const std::string& url,
+ const std::string& data,
+ Receivable *dest,
+ const Header& hdr )
+{
+ long response ;
+ while ( CheckRetry(
+ response = m_agent->Put(url, data, dest, AppendHeader(hdr)) ) ) ;
+
+ return response ;
+}
+
+long AuthAgent::Put(
+ const std::string& url,
+ StdioFile& file,
+ Receivable *dest,
+ const Header& hdr )
+{
+ long response ;
+ while ( CheckRetry(
+ response = m_agent->Put( url, file, dest, AppendHeader(hdr) ) ) ) ;
+
+ return response ;
+}
+
+long AuthAgent::Get(
+ const std::string& url,
+ Receivable *dest,
+ const Header& hdr )
+{
+ long response ;
+ while ( CheckRetry(
+ response = m_agent->Get( url, dest, AppendHeader(hdr) ) ) ) ;
+
+ return response ;
+}
+
+long AuthAgent::Post(
+ const std::string& url,
+ const std::string& data,
+ Receivable *dest,
+ const Header& hdr )
+{
+ long response ;
+ while ( CheckRetry(
+ response = m_agent->Post( url, data, dest, AppendHeader(hdr) ) ) ) ;
+
+ return response ;
+}
+
+long AuthAgent::Custom(
+ const std::string& method,
+ const std::string& url,
+ Receivable *dest,
+ const Header& hdr )
+{
+ long response ;
+ while ( CheckRetry(
+ response = m_agent->Custom( method, url, dest, AppendHeader(hdr) ) ) ) ;
+
+ return response ;
+}
+
+std::string AuthAgent::RedirLocation() const
+{
+ return m_agent->RedirLocation() ;
+}
+
+std::string AuthAgent::Escape( const std::string& str )
+{
+ return m_agent->Escape( str ) ;
+}
+
+std::string AuthAgent::Unescape( const std::string& str )
+{
+ return m_agent->Unescape( str ) ;
+}
+
+bool AuthAgent::CheckRetry( long response )
+{
+ if ( response == 500 || response == 503 )
+ {
+ Log( "resquest failed due to temperory error: %1%. retrying in 5 seconds",
+ response, log::warning ) ;
+
+ os::Sleep( 5 ) ;
+ return true ;
+ }
+ else
+ return false ;
+}
+
+} // end of namespace
View
82 libgrive/src/protocol/AuthAgent.hh
@@ -0,0 +1,82 @@
+/*
+ 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 "http/Agent.hh"
+#include "OAuth2.hh"
+
+#include <memory>
+
+namespace gr {
+
+/*! \brief An HTTP agent with support OAuth2
+
+ This is a HTTP agent that provide support for OAuth2. It will also perform retries on
+ certain HTTP errors.
+*/
+class AuthAgent : public http::Agent
+{
+public :
+ AuthAgent( const OAuth2& auth, std::auto_ptr<http::Agent> real_agent ) ;
+
+ long Put(
+ const std::string& url,
+ const std::string& data,
+ http::Receivable *dest,
+ const http::Header& hdr ) ;
+
+ long Put(
+ const std::string& url,
+ StdioFile& file,
+ http::Receivable *dest,
+ const http::Header& hdr ) ;
+
+ long Get(
+ const std::string& url,
+ http::Receivable *dest,
+ const http::Header& hdr ) ;
+
+ long Post(
+ const std::string& url,
+ const std::string& data,
+ http::Receivable *dest,
+ const http::Header& hdr ) ;
+
+ long Custom(
+ const std::string& method,
+ const std::string& url,
+ http::Receivable *dest,
+ const http::Header& hdr ) ;
+
+ std::string RedirLocation() const ;
+
+ std::string Escape( const std::string& str ) ;
+ std::string Unescape( const std::string& str ) ;
+
+private :
+ http::Header AppendHeader( const http::Header& hdr ) const ;
+ bool CheckRetry( long response ) ;
+
+private :
+ OAuth2 m_auth ;
+ const std::auto_ptr<http::Agent> m_agent ;
+} ;
+
+} // end of namespace
View
13 libgrive/src/util/OS.cc
@@ -81,4 +81,17 @@ void SetFileTime( const std::string& filename, const DateTime& t )
) ;
}
+void Sleep( unsigned int sec )
+{
+ struct timespec ts = { sec, 0 } ;
+
+ int result = 0 ;
+ do
+ {
+ struct timespec rem ;
+ nanosleep( &ts, &rem ) ;
+ ts = rem ;
+ } while ( result == -1 && errno == EINTR ) ;
+}
+
} } // end of namespaces
View
2  libgrive/src/util/OS.hh
@@ -38,6 +38,8 @@ namespace os
void SetFileTime( const std::string& filename, const DateTime& t ) ;
void SetFileTime( const fs::path& filename, const DateTime& t ) ;
+
+ void Sleep( unsigned int sec ) ;
}
} // end of namespaces
Please sign in to comment.
Something went wrong with that request. Please try again.