2012-05-16 18:35:22 +04:00
|
|
|
/*
|
|
|
|
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 "State.hh"
|
|
|
|
|
2015-05-17 16:54:04 +03:00
|
|
|
#include "Entry.hh"
|
|
|
|
#include "Resource.hh"
|
|
|
|
#include "Syncer.hh"
|
2012-05-17 20:37:11 +04:00
|
|
|
|
2012-05-16 18:35:22 +04:00
|
|
|
#include "util/Crypt.hh"
|
2013-04-28 14:40:21 +04:00
|
|
|
#include "util/File.hh"
|
2012-06-03 14:31:02 +04:00
|
|
|
#include "util/log/Log.hh"
|
2015-05-16 00:27:59 +03:00
|
|
|
#include "json/JsonParser.hh"
|
2012-05-16 18:35:22 +04:00
|
|
|
|
2012-05-16 20:52:17 +04:00
|
|
|
#include <fstream>
|
2012-05-16 18:35:22 +04:00
|
|
|
|
2015-05-17 16:54:04 +03:00
|
|
|
namespace gr {
|
2012-05-16 20:52:17 +04:00
|
|
|
|
2017-03-19 19:54:03 +03:00
|
|
|
const std::string state_file = ".grive_state" ;
|
|
|
|
const std::string ignore_file = ".griveignore" ;
|
|
|
|
const int MAX_IGN = 65536 ;
|
|
|
|
const char* regex_escape_chars = ".^$|()[]{}*+?\\";
|
|
|
|
const boost::regex regex_escape_re( "[.^$|()\\[\\]{}*+?\\\\]" );
|
|
|
|
|
|
|
|
inline std::string regex_escape( std::string s )
|
|
|
|
{
|
|
|
|
return regex_replace( s, regex_escape_re, "\\\\&", boost::format_sed );
|
|
|
|
}
|
|
|
|
|
|
|
|
State::State( const fs::path& root, const Val& options ) :
|
|
|
|
m_root ( root ),
|
2015-05-17 16:54:04 +03:00
|
|
|
m_res ( options["path"].Str() ),
|
2015-12-12 16:59:02 +03:00
|
|
|
m_cstamp ( -1 )
|
2012-05-16 20:52:17 +04:00
|
|
|
{
|
2017-03-19 19:54:03 +03:00
|
|
|
Read() ;
|
|
|
|
|
2016-01-02 15:05:37 +03:00
|
|
|
// the "-f" option will make grive always think remote is newer
|
|
|
|
m_force = options.Has( "force" ) ? options["force"].Bool() : false ;
|
2017-03-19 19:54:03 +03:00
|
|
|
|
2016-05-11 15:56:59 +03:00
|
|
|
std::string m_orig_ign = m_ign;
|
2015-12-28 02:00:47 +03:00
|
|
|
if ( options.Has( "ignore" ) && options["ignore"].Str() != m_ign )
|
2015-12-12 16:59:02 +03:00
|
|
|
m_ign = options["ignore"].Str();
|
2015-12-12 17:16:05 +03:00
|
|
|
else if ( options.Has( "dir" ) )
|
|
|
|
{
|
|
|
|
const boost::regex trim_path( "^/+|/+$" );
|
|
|
|
std::string m_dir = regex_replace( options["dir"].Str(), trim_path, "" );
|
|
|
|
if ( !m_dir.empty() )
|
|
|
|
{
|
|
|
|
// "-s" is internally converted to an ignore regexp
|
2017-03-19 19:54:03 +03:00
|
|
|
m_dir = regex_escape( m_dir );
|
2016-05-10 23:10:41 +03:00
|
|
|
size_t pos = 0;
|
|
|
|
while ( ( pos = m_dir.find( '/', pos ) ) != std::string::npos )
|
|
|
|
{
|
|
|
|
m_dir = m_dir.substr( 0, pos ) + "$|" + m_dir;
|
|
|
|
pos = pos*2 + 3;
|
|
|
|
}
|
|
|
|
std::string ign = "^(?!"+m_dir+"(/|$))";
|
2015-12-12 17:16:05 +03:00
|
|
|
m_ign = ign;
|
|
|
|
}
|
|
|
|
}
|
2016-05-11 15:56:59 +03:00
|
|
|
|
|
|
|
m_ign_changed = m_orig_ign != "" && m_orig_ign != m_ign;
|
2017-03-19 19:54:03 +03:00
|
|
|
m_ign_re = boost::regex( m_ign.empty() ? "^\\.(grive$|grive_state$|trash)" : ( m_ign+"|^\\.(grive|grive_state|trash)" ) );
|
2012-05-16 20:52:17 +04:00
|
|
|
}
|
|
|
|
|
2012-06-10 12:18:21 +04:00
|
|
|
State::~State()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2012-05-19 21:44:46 +04:00
|
|
|
/// Synchronize local directory. Build up the resource tree from files and folders
|
|
|
|
/// of local directory.
|
|
|
|
void State::FromLocal( const fs::path& p )
|
2012-05-16 20:52:17 +04:00
|
|
|
{
|
2016-01-01 22:04:39 +03:00
|
|
|
m_res.Root()->FromLocal( m_st ) ;
|
2016-01-02 15:04:28 +03:00
|
|
|
FromLocal( p, m_res.Root(), m_st.Item( "tree" ) ) ;
|
2012-05-17 20:37:11 +04:00
|
|
|
}
|
|
|
|
|
2012-06-06 19:35:29 +04:00
|
|
|
bool State::IsIgnore( const std::string& filename )
|
|
|
|
{
|
2015-12-12 16:59:02 +03:00
|
|
|
return regex_search( filename.c_str(), m_ign_re );
|
2012-06-06 19:35:29 +04:00
|
|
|
}
|
|
|
|
|
2016-01-01 02:27:21 +03:00
|
|
|
void State::FromLocal( const fs::path& p, Resource* folder, Val& tree )
|
2012-05-17 20:37:11 +04:00
|
|
|
{
|
2012-05-19 11:26:55 +04:00
|
|
|
assert( folder != 0 ) ;
|
2012-05-19 14:03:02 +04:00
|
|
|
assert( folder->IsFolder() ) ;
|
2015-10-08 13:30:50 +03:00
|
|
|
|
2016-01-02 15:04:28 +03:00
|
|
|
Val::Object leftover = tree.AsObject();
|
|
|
|
|
2012-05-16 20:52:17 +04:00
|
|
|
for ( fs::directory_iterator i( p ) ; i != fs::directory_iterator() ; ++i )
|
|
|
|
{
|
2013-04-28 14:40:21 +04:00
|
|
|
std::string fname = i->path().filename().string() ;
|
2015-09-25 13:01:26 +03:00
|
|
|
std::string path = folder->IsRoot() ? fname : ( folder->RelPath() / fname ).string();
|
2016-01-01 02:27:21 +03:00
|
|
|
|
2015-09-25 13:01:26 +03:00
|
|
|
if ( IsIgnore( path ) )
|
|
|
|
Log( "file %1% is ignored by grive", path, log::verbose ) ;
|
2012-05-19 21:44:46 +04:00
|
|
|
else
|
2012-05-19 14:03:02 +04:00
|
|
|
{
|
2012-05-20 19:16:59 +04:00
|
|
|
// if the Resource object of the child already exists, it should
|
|
|
|
// have been so no need to do anything here
|
2016-09-28 01:19:13 +03:00
|
|
|
Resource *c = folder->FindChild( fname ), *c2 = c ;
|
2016-01-01 02:27:21 +03:00
|
|
|
if ( !c )
|
2012-05-20 13:12:01 +04:00
|
|
|
{
|
2016-09-28 01:19:13 +03:00
|
|
|
c2 = new Resource( fname, "" ) ;
|
|
|
|
folder->AddChild( c2 ) ;
|
2012-05-20 13:12:01 +04:00
|
|
|
}
|
2016-01-02 15:04:28 +03:00
|
|
|
leftover.erase( fname );
|
|
|
|
Val& rec = tree.Item( fname );
|
2016-01-02 15:05:37 +03:00
|
|
|
if ( m_force )
|
|
|
|
rec.Del( "srv_time" );
|
2016-09-28 01:19:13 +03:00
|
|
|
c2->FromLocal( rec ) ;
|
|
|
|
if ( !c )
|
|
|
|
m_res.Insert( c2 ) ;
|
|
|
|
if ( c2->IsFolder() )
|
|
|
|
FromLocal( *i, c2, rec.Item( "tree" ) ) ;
|
2016-01-02 15:04:28 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for( Val::Object::iterator i = leftover.begin(); i != leftover.end(); i++ )
|
|
|
|
{
|
2016-05-11 15:56:59 +03:00
|
|
|
std::string path = folder->IsRoot() ? i->first : ( folder->RelPath() / i->first ).string();
|
|
|
|
if ( IsIgnore( path ) )
|
|
|
|
Log( "file %1% is ignored by grive", path, log::verbose ) ;
|
|
|
|
else
|
2016-01-02 15:04:28 +03:00
|
|
|
{
|
2016-05-11 15:56:59 +03:00
|
|
|
// Restore state of locally deleted files
|
2016-09-28 01:19:13 +03:00
|
|
|
Resource *c = folder->FindChild( i->first ), *c2 ;
|
2016-05-11 15:56:59 +03:00
|
|
|
if ( !c )
|
|
|
|
{
|
2016-09-28 01:19:13 +03:00
|
|
|
c2 = new Resource( i->first, i->second.Has( "tree" ) ? "folder" : "file" ) ;
|
|
|
|
folder->AddChild( c2 ) ;
|
2016-05-11 15:56:59 +03:00
|
|
|
}
|
|
|
|
Val& rec = tree.Item( i->first );
|
|
|
|
if ( m_force || m_ign_changed )
|
|
|
|
rec.Del( "srv_time" );
|
2016-09-28 01:19:13 +03:00
|
|
|
c2->FromDeleted( rec );
|
|
|
|
if ( !c )
|
|
|
|
m_res.Insert( c2 ) ;
|
2012-05-19 14:03:02 +04:00
|
|
|
}
|
2012-05-16 18:35:22 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-19 21:44:46 +04:00
|
|
|
void State::FromRemote( const Entry& e )
|
2012-05-17 20:37:11 +04:00
|
|
|
{
|
2015-05-16 12:35:41 +03:00
|
|
|
std::string fn = e.Filename() ;
|
|
|
|
std::string k = e.IsDir() ? "folder" : "file";
|
2012-06-14 20:55:00 +04:00
|
|
|
|
|
|
|
// common checkings
|
2016-01-01 02:27:21 +03:00
|
|
|
if ( !e.IsDir() && ( fn.empty() || e.ContentSrc().empty() ) )
|
2015-05-16 12:35:41 +03:00
|
|
|
Log( "%1% \"%2%\" is a google document, ignored", k, e.Name(), log::verbose ) ;
|
2012-06-14 20:55:00 +04:00
|
|
|
|
|
|
|
else if ( fn.find('/') != fn.npos )
|
2015-05-16 12:35:41 +03:00
|
|
|
Log( "%1% \"%2%\" contains a slash in its name, ignored", k, e.Name(), log::verbose ) ;
|
2012-06-14 20:55:00 +04:00
|
|
|
|
|
|
|
else if ( !e.IsChange() && e.ParentHrefs().size() != 1 )
|
2015-05-16 12:35:41 +03:00
|
|
|
Log( "%1% \"%2%\" has multiple parents, ignored", k, e.Name(), log::verbose ) ;
|
2012-06-14 20:55:00 +04:00
|
|
|
|
2012-06-10 17:03:32 +04:00
|
|
|
else if ( e.IsChange() )
|
|
|
|
FromChange( e ) ;
|
2012-06-14 20:55:00 +04:00
|
|
|
|
2012-06-06 19:35:29 +04:00
|
|
|
else if ( !Update( e ) )
|
2012-05-19 13:14:04 +04:00
|
|
|
m_unresolved.push_back( e ) ;
|
2012-05-17 20:37:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void State::ResolveEntry()
|
|
|
|
{
|
2012-05-19 13:14:04 +04:00
|
|
|
while ( !m_unresolved.empty() )
|
2012-05-17 20:37:11 +04:00
|
|
|
{
|
|
|
|
if ( TryResolveEntry() == 0 )
|
|
|
|
break ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::size_t State::TryResolveEntry()
|
|
|
|
{
|
2012-05-19 13:14:04 +04:00
|
|
|
assert( !m_unresolved.empty() ) ;
|
2012-05-17 20:37:11 +04:00
|
|
|
|
|
|
|
std::size_t count = 0 ;
|
2016-01-03 16:24:55 +03:00
|
|
|
std::list<Entry>& en = m_unresolved ;
|
|
|
|
|
|
|
|
for ( std::list<Entry>::iterator i = en.begin() ; i != en.end() ; )
|
2012-05-17 20:37:11 +04:00
|
|
|
{
|
|
|
|
if ( Update( *i ) )
|
|
|
|
{
|
|
|
|
i = en.erase( i ) ;
|
|
|
|
count++ ;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
++i ;
|
|
|
|
}
|
|
|
|
return count ;
|
|
|
|
}
|
|
|
|
|
2012-06-04 20:32:12 +04:00
|
|
|
void State::FromChange( const Entry& e )
|
|
|
|
{
|
2012-06-10 17:03:32 +04:00
|
|
|
assert( e.IsChange() ) ;
|
2012-06-06 19:35:29 +04:00
|
|
|
|
2012-06-05 19:08:03 +04:00
|
|
|
// entries in the change feed is always treated as newer in remote,
|
2012-06-04 20:32:12 +04:00
|
|
|
// so we override the last sync time to 0
|
2015-05-16 12:35:41 +03:00
|
|
|
if ( Resource *res = m_res.FindByHref( e.SelfHref() ) )
|
2016-01-01 22:04:39 +03:00
|
|
|
m_res.Update( res, e ) ;
|
2012-06-04 20:32:12 +04:00
|
|
|
}
|
|
|
|
|
2012-05-17 20:37:11 +04:00
|
|
|
bool State::Update( const Entry& e )
|
|
|
|
{
|
2012-06-10 17:03:32 +04:00
|
|
|
assert( !e.IsChange() ) ;
|
2012-05-19 14:27:53 +04:00
|
|
|
assert( !e.ParentHref().empty() ) ;
|
2012-06-03 11:55:32 +04:00
|
|
|
|
2012-05-20 15:28:44 +04:00
|
|
|
if ( Resource *res = m_res.FindByHref( e.SelfHref() ) )
|
2012-05-20 13:12:01 +04:00
|
|
|
{
|
2015-09-25 13:01:26 +03:00
|
|
|
std::string path = res->RelPath().string();
|
|
|
|
if ( IsIgnore( path ) )
|
|
|
|
{
|
|
|
|
Log( "%1% is ignored by grive", path, log::verbose ) ;
|
|
|
|
return true;
|
|
|
|
}
|
2016-01-01 22:04:39 +03:00
|
|
|
m_res.Update( res, e ) ;
|
2015-10-11 00:43:54 +03:00
|
|
|
return true;
|
2012-05-20 13:12:01 +04:00
|
|
|
}
|
2012-05-20 15:28:44 +04:00
|
|
|
else if ( Resource *parent = m_res.FindByHref( e.ParentHref() ) )
|
2012-05-17 20:37:11 +04:00
|
|
|
{
|
2012-05-19 14:03:02 +04:00
|
|
|
assert( parent->IsFolder() ) ;
|
2012-05-20 10:57:25 +04:00
|
|
|
|
2015-09-25 13:01:26 +03:00
|
|
|
std::string path = parent->IsRoot() ? e.Name() : ( parent->RelPath() / e.Name() ).string();
|
|
|
|
if ( IsIgnore( path ) )
|
|
|
|
{
|
|
|
|
Log( "%1% is ignored by grive", path, log::verbose ) ;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-05-17 20:37:11 +04:00
|
|
|
// see if the entry already exist in local
|
2012-06-10 17:03:32 +04:00
|
|
|
std::string name = e.Name() ;
|
2012-05-20 10:57:25 +04:00
|
|
|
Resource *child = parent->FindChild( name ) ;
|
2016-01-01 02:27:21 +03:00
|
|
|
if ( child )
|
2012-05-17 20:37:11 +04:00
|
|
|
{
|
|
|
|
// since we are updating the ID and Href, we need to remove it and re-add it.
|
2016-01-01 22:04:39 +03:00
|
|
|
m_res.Update( child, e ) ;
|
2012-05-17 20:37:11 +04:00
|
|
|
}
|
|
|
|
|
2012-05-19 12:30:04 +04:00
|
|
|
// folder entry exist in google drive, but not local. we should create
|
|
|
|
// the directory
|
2015-05-16 12:35:41 +03:00
|
|
|
else if ( e.IsDir() || !e.Filename().empty() )
|
2012-05-17 20:37:11 +04:00
|
|
|
{
|
2012-05-31 15:22:18 +04:00
|
|
|
// first create a dummy resource and update it later
|
2015-05-16 12:35:41 +03:00
|
|
|
child = new Resource( name, e.IsDir() ? "folder" : "file" ) ;
|
2012-05-30 21:17:22 +04:00
|
|
|
parent->AddChild( child ) ;
|
|
|
|
m_res.Insert( child ) ;
|
2012-05-31 15:22:18 +04:00
|
|
|
|
|
|
|
// update the state of the resource
|
2016-01-01 22:04:39 +03:00
|
|
|
m_res.Update( child, e ) ;
|
2012-05-17 20:37:11 +04:00
|
|
|
}
|
2012-05-20 10:57:25 +04:00
|
|
|
|
2012-05-17 20:37:11 +04:00
|
|
|
return true ;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return false ;
|
|
|
|
}
|
|
|
|
|
2012-05-24 13:47:26 +04:00
|
|
|
Resource* State::FindByHref( const std::string& href )
|
2012-05-17 20:37:11 +04:00
|
|
|
{
|
2012-05-20 11:25:38 +04:00
|
|
|
return m_res.FindByHref( href ) ;
|
2012-05-19 13:14:04 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
State::iterator State::begin()
|
|
|
|
{
|
2012-05-20 11:25:38 +04:00
|
|
|
return m_res.begin() ;
|
2012-05-19 13:14:04 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
State::iterator State::end()
|
|
|
|
{
|
2012-05-20 11:25:38 +04:00
|
|
|
return m_res.end() ;
|
|
|
|
}
|
|
|
|
|
2017-03-19 19:54:03 +03:00
|
|
|
void State::Read()
|
2012-05-20 11:25:38 +04:00
|
|
|
{
|
2012-05-29 21:30:06 +04:00
|
|
|
try
|
2012-05-20 13:12:01 +04:00
|
|
|
{
|
2017-03-19 19:54:03 +03:00
|
|
|
File st_file( m_root / state_file ) ;
|
|
|
|
m_st = ParseJson( st_file );
|
2016-01-01 02:27:21 +03:00
|
|
|
m_cstamp = m_st["change_stamp"].Int() ;
|
2012-05-29 21:30:06 +04:00
|
|
|
}
|
|
|
|
catch ( Exception& )
|
|
|
|
{
|
2012-05-20 13:12:01 +04:00
|
|
|
}
|
2017-03-19 19:54:03 +03:00
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
File ign_file( m_root / ignore_file ) ;
|
|
|
|
char ign[MAX_IGN] = { 0 };
|
|
|
|
int s = ign_file.Read( ign, MAX_IGN-1 ) ;
|
|
|
|
ParseIgnoreFile( ign, s );
|
|
|
|
}
|
|
|
|
catch ( Exception& e )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool State::ParseIgnoreFile( const char* buffer, int size )
|
|
|
|
{
|
|
|
|
const boost::regex re1( "/\\\\\\*\\\\\\*$" );
|
|
|
|
const boost::regex re2( "^\\\\\\*\\\\\\*/" );
|
|
|
|
const boost::regex re3( "([^\\\\](\\\\\\\\)*)/\\\\\\*\\\\\\*/" );
|
|
|
|
const boost::regex re4( "([^\\\\](\\\\\\\\)*|^)\\\\\\*" );
|
|
|
|
const boost::regex re5( "([^\\\\](\\\\\\\\)*|^)\\\\\\?" );
|
|
|
|
std::string exclude_re, include_re;
|
|
|
|
int prev = 0;
|
|
|
|
for ( int i = 0; i <= size; i++ )
|
|
|
|
{
|
|
|
|
if ( buffer[i] == '\n' || ( i == size && i > prev ) )
|
|
|
|
{
|
|
|
|
while ( prev < i && ( buffer[prev] == ' ' || buffer[prev] == '\t' || buffer[prev] == '\r' ) )
|
|
|
|
prev++;
|
|
|
|
if ( buffer[prev] != '#' )
|
|
|
|
{
|
|
|
|
int j;
|
|
|
|
for ( j = i-1; j > prev; j-- )
|
|
|
|
if ( buffer[j-1] == '\\' || ( buffer[j] != ' ' && buffer[j] != '\t' && buffer[j] != '\r' ) )
|
|
|
|
break;
|
|
|
|
std::string str( buffer+prev, j+1-prev );
|
|
|
|
bool inc = str[0] == '!';
|
|
|
|
if ( inc )
|
|
|
|
str = str.substr( 1 );
|
|
|
|
str = regex_escape( str );
|
|
|
|
str = regex_replace( str, re1, "/.*", boost::format_perl );
|
|
|
|
str = regex_replace( str, re2, ".*/", boost::format_perl );
|
|
|
|
str = regex_replace( str, re3, "$1/(.*/)*", boost::format_perl );
|
|
|
|
str = regex_replace( str, re4, "$1[^/]*", boost::format_perl );
|
|
|
|
std::string str1;
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
str1 = regex_replace( str, re5, "$1[^/]", boost::format_perl );
|
|
|
|
if ( str1.size() == str.size() )
|
|
|
|
break;
|
|
|
|
str = str1;
|
|
|
|
}
|
|
|
|
if ( !inc )
|
|
|
|
exclude_re = exclude_re + ( exclude_re.size() > 0 ? "|" : "" ) + str;
|
|
|
|
else
|
|
|
|
include_re = include_re + ( include_re.size() > 0 ? "|" : "" ) + str;
|
|
|
|
}
|
|
|
|
prev = i+1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( exclude_re.size() > 0 )
|
|
|
|
{
|
|
|
|
m_ign = "^" + ( include_re.size() > 0 ? "(?!" + include_re + ")" : std::string() ) + "(" + exclude_re + ")$";
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2012-05-20 11:25:38 +04:00
|
|
|
}
|
|
|
|
|
2017-03-19 19:54:03 +03:00
|
|
|
void State::Write()
|
2012-05-20 11:25:38 +04:00
|
|
|
{
|
2016-01-01 02:27:21 +03:00
|
|
|
m_st.Set( "change_stamp", Val( m_cstamp ) ) ;
|
|
|
|
m_st.Set( "ignore_regexp", Val( m_ign ) ) ;
|
2012-05-20 11:25:38 +04:00
|
|
|
|
2017-03-19 19:54:03 +03:00
|
|
|
fs::path filename = m_root / state_file ;
|
2012-05-20 11:25:38 +04:00
|
|
|
std::ofstream fs( filename.string().c_str() ) ;
|
2016-01-01 02:27:21 +03:00
|
|
|
fs << m_st ;
|
2012-05-17 20:37:11 +04:00
|
|
|
}
|
2012-05-16 21:20:02 +04:00
|
|
|
|
2015-05-17 02:00:00 +03:00
|
|
|
void State::Sync( Syncer *syncer, const Val& options )
|
2012-05-29 21:30:06 +04:00
|
|
|
{
|
2012-07-15 20:33:33 +04:00
|
|
|
// set the last sync time to the time on the client
|
2016-01-03 01:57:54 +03:00
|
|
|
m_res.Root()->Sync( syncer, &m_res, options ) ;
|
2012-05-29 21:30:06 +04:00
|
|
|
}
|
|
|
|
|
2012-06-03 19:58:28 +04:00
|
|
|
long State::ChangeStamp() const
|
|
|
|
{
|
|
|
|
return m_cstamp ;
|
|
|
|
}
|
|
|
|
|
|
|
|
void State::ChangeStamp( long cstamp )
|
|
|
|
{
|
2012-06-25 20:13:18 +04:00
|
|
|
Log( "change stamp is set to %1%", cstamp, log::verbose ) ;
|
2012-06-03 19:58:28 +04:00
|
|
|
m_cstamp = cstamp ;
|
|
|
|
}
|
|
|
|
|
2015-05-17 16:54:04 +03:00
|
|
|
} // end of namespace gr
|