|
|
#hey true blue
d:\projects\src\junk
d:\projects\test\backup\junk
true
#------------
d:\projects\src\alphacalc
d:\projects\test\backup\alphacalc
false
#-- has embedded white space
C:\My Installations
d:\projects\test\backup\installs
true
|
|
|
#include <string>
#include <algorithm>
#include <stack>
using namespace std;
#include <ctype.h>
#include "globals.h"
#include "CompareDirTrees.h"
#include "FileSystem.h"
class DirsToDo
{
public:
void Init()
{
mDirsToDo.push(".");
}
bool More()
{
return !mDirsToDo.empty();
}
string Next()
{
string dir = mDirsToDo.top();
mDirsToDo.pop();
return dir;
}
void PushAllSubDirs(const string& root, const string& dir)
{
FileSystem::PathList dirlist = FileSystem::GetDirs(dir == "" ? root : root + "\\" + dir);
for(FileSystem::PathList::iterator it = dirlist.begin(); it != dirlist.end(); ++it)
mDirsToDo.push(dir == "" ? *it : dir + "\\" + *it);
}
private:
stack<string> mDirsToDo;
} ;
class Dirs
{
public:
Dirs(const string& dir1, const string& dir2, CompareDirTrees::Functor& fn)
: masterroot(dir1), slaveroot(dir2), mCallback(fn)
{
}
void Compare()
{
masterfiles = FileSystem::GetFiles(masterroot);
slavefiles = FileSystem::GetFiles(slaveroot);
CompareFileLists();
}
private:
void CompareFileLists()
{
for(FileSystem::PathList::const_iterator it = masterfiles.begin(); it != masterfiles.end(); ++it)
CompareFile(*it);
}
void CompareFile(const string& file)
{
if (SameFile(file)) return;
mCallback(masterroot, slaveroot, file);
}
bool SameFile(const string& file)
{
return FileInBoth(file) && FilesAreEqual(file);
}
bool FileInBoth(const string& file)
{
//must compare case-insensitive
FileSystem::PathList::const_iterator it;
for(it = slavefiles.begin(); it != slavefiles.end(); ++it)
{
if (ToLower(*it) == ToLower(file))
break;
}
if (it != slavefiles.end())
return true;
globals.Log << now
<< "Found new file: " + file
<< endl;
return false;
}
bool FilesAreEqual(const string& file)
{
return FileSystem::FilesAreEqual(masterroot, slaveroot, file);
}
string ToLower(const string& s)
{
string r = s;
transform(r.begin(), r.end(), r.begin(), ::tolower);
return r;
}
FileSystem::PathList masterfiles;
FileSystem::PathList slavefiles;
string masterroot;
string slaveroot;
CompareDirTrees::Functor& mCallback;
} ;
class CompareDirTrees::Private
{
public:
void Init(const string& masterdir, const string& slavedir)
{
masterroot = masterdir;
slaveroot = slavedir;
masterrelativeroot = "";
slaverelativeroot = "";
}
void AddNextNode(const string& dir)
{
if (dir == ".") return;
masterrelativeroot = dir;
slaverelativeroot = dir;
}
void CreateSlaveDir()
{
if (FileSystem::DirExists(SlavePath())) return;
FileSystem::CreateDir(SlavePath());
}
void CompareDirs(Functor& fn)
{
Dirs dirs(MasterPath(), SlavePath(), fn);
dirs.Compare();
}
string masterrelativeroot;
string slaverelativeroot;
private:
string MasterPath()
{
return masterroot + "\\" + masterrelativeroot;
}
string SlavePath()
{
return slaveroot + "\\" + slaverelativeroot;
}
private:
string masterroot;
string slaveroot;
} ;
CompareDirTrees::CompareDirTrees()
: impl(* new CompareDirTrees::Private)
{
}
CompareDirTrees::~CompareDirTrees()
{
delete &impl;
}
void CompareDirTrees::Compare(const string& masterdir, const string& slavedir, bool subdirs, Functor& fn)
{
impl.Init(masterdir, slavedir);
DirsToDo dirs;
dirs.Init();
while (dirs.More())
{
string dir = dirs.Next();
impl.AddNextNode(dir);
impl.CreateSlaveDir();
impl.CompareDirs(fn);
if (subdirs)
dirs.PushAllSubDirs(masterdir, impl.masterrelativeroot);
}
}
|
|
|
#include <string>
using std::string;
class CompareDirTrees
{
public:
struct Functor
{
virtual void operator() (const string& masterdir, const string& slavedir, const string& filename) = 0;
} ;
CompareDirTrees();
~CompareDirTrees();
void Compare(const string& masterdir, const string& slavedir, bool subdirs, Functor& fn);
private:
class Private;
Private& impl;
} ;
|
|
|
#include "globals.h"
#include "ConfigParser.h"
#include "ConfigFile.h"
#include "Notification.h"
ConfigFile::ConfigFile(Notification& notify)
: mNotify(notify)
{
}
void ConfigFile::operator() (const ConfigParser::ValueList& values)
{
globals.Log << now
<< "dir=" << values[0]
<< " backupdir=" << values[1]
<< " subdirs=" << values[2]
<< endl;
mNotify.Add(values[0],
(values[2] == "true") ? true : false,
values[1]);
}
void ConfigFile::Load()
{
ConfigParser parser(string("jContinuousBackup.cfg"), 3);
parser.parse(*this);
}
|
|
|
#pragma once
#include "ConfigParser.h"
class Notification;
class ConfigFile : public ConfigParser::Functor
{
public:
ConfigFile(Notification& notify);
void operator() (const ConfigParser::ValueList& values);
void Load();
private:
Notification& mNotify;
} ;
|
|
|
#include <string>
#include <vector>
#include <fstream>
#include <iostream>
using namespace std;
#include "ConfigParser.h"
class ConfigParser::Private
{
public:
Private(const string& fname, int numitems)
: mState(0), mNumLinesPerItem(numitems), mFilename(fname)
{
}
void Reset()
{
mState = 0;
mValues.clear();
}
bool More()
{
if (mFile.eof()) return false;
GetLine();
return true;
}
bool OkToCallback()
{
return mState == mNumLinesPerItem;
}
void Close()
{
if (!mFile.is_open()) return;
mFile.close();
}
void Open()
{
if (mFile.is_open()) return;
mFile.open(mFilename.c_str());
//mFile.ipfx(false);
}
ValueList mValues;
private:
bool IsIgnoredLine(const string& val)
{
unsigned posn = val.find_first_not_of(" \t\r");
return posn == string::npos || val[posn] == '#';
}
void SaveLine(const string& val)
{
mValues.push_back(Trim(val));
mState++;
}
string Trim(const string& val)
{
size_t start = val.find_first_not_of(" \t\r");
size_t end = val.find_last_not_of(" \t\r");
return val.substr(start, end - start + 1);
}
void GetLine()
{
string val;
getline(mFile, val);
if (IsIgnoredLine(val)) return;
SaveLine(val);
}
int mState;
int mNumLinesPerItem;
string mFilename;
string mValue;
ifstream mFile;
} ;
ConfigParser::ConfigParser(const string& fname, int numitems)
: impl(* new ConfigParser::Private(fname, numitems))
{
impl.Open();
}
ConfigParser::~ConfigParser()
{
impl.Close();
delete &impl;
}
void ConfigParser::parse(Functor& callback)
{
impl.Reset();
while(impl.More())
{
if (!impl.OkToCallback()) continue;
callback(impl.mValues);
impl.Reset();
}
impl.Close();
}
|
|
|
#pragma once
#include <vector>
#include <string>
using std::string;
using std::vector;
class ConfigParser
{
public:
typedef vector<string> ValueList;
struct Functor
{
virtual void operator() (const ValueList& values) = 0;
} ;
ConfigParser(const string& fname, int numitems);
~ConfigParser();
void parse(Functor& callback);
private:
class Private;
Private& impl;
};
|
|
|
#pragma once
#include <string>
using std::string;
struct DirInfo
{
bool subdirs;
string dir;
string backupdir;
} ;
|
|
|
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <fstream>
using std::ifstream;
#include "globals.h"
#include "FileSystem.h"
void FileSystem::CreateDir(const string& dir)
{
if (DirExists(dir))
return;
//for each node in the path create the directory
int index = -1;
for(;;)
{
index = dir.find('\\', index + 1);
if ((unsigned)index == string::npos)
break;
string p = dir.substr(0, index);
if (!DirExists(p))
::CreateDirectory(p.c_str(), 0);
}
if (!DirExists(dir))
::CreateDirectory(dir.c_str(), 0);
}
bool FileSystem::DirExists(const string& dir)
{
DWORD res = ::GetFileAttributes(dir.c_str());
if (res == 0xFFFFFFFF) return false;
if (res & FILE_ATTRIBUTE_DIRECTORY) return true;
return false;
}
FileSystem::PathList FileSystem::GetDirs(const string& dir)
{
PathList subdirlist;
WIN32_FIND_DATA fdata;
string fspec = dir + "\\*.*";
HANDLE hfile = FindFirstFile(fspec.c_str(), &fdata);
if (hfile == INVALID_HANDLE_VALUE)
return subdirlist;
for (BOOL rc = TRUE; rc; rc = FindNextFile(hfile, &fdata))
{
string f= fdata.cFileName;
if (f == string(".") || f == string(".."))
continue;
if ((fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
continue;
subdirlist.push_back(f);
}
FindClose(hfile);
return subdirlist;
}
FileSystem::PathList FileSystem::GetFiles(const string& rootdir)
{
PathList filelist;
WIN32_FIND_DATA fdata;
string fspec = rootdir + "\\*.*";
HANDLE hfile = FindFirstFile(fspec.c_str(), &fdata);
if (hfile == INVALID_HANDLE_VALUE)
return filelist;
for (BOOL rc = TRUE; rc; rc = FindNextFile(hfile, &fdata))
{
string f= fdata.cFileName;
if (f == string(".") || f == string(".."))
continue;
if ((fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
continue;
filelist.push_back(f);
}
FindClose(hfile);
return filelist;
}
bool FileSystem::FilesAreEqual(const string& masterdir, const string& slavedir, const string& fname)
{
unsigned long masterhash = GetHash(masterdir + "\\" + fname);
unsigned long slavehash = GetHash(slavedir + "\\" + fname);
if (masterhash == slavehash)
return true;
globals.Log << now
<< "file=" << masterdir + "\\" + fname
<< " has changed: masterhash=" << masterhash
<< " slavehash=" << slavehash
<< endl;
return false;
}
unsigned long FileSystem::GetHash(const string& path)
{
ifstream f(path.c_str());
if (!f.is_open())
return 0xFFFFFFFF;
unsigned long hash = 0;
while(!f.eof())
{
unsigned long buf[120];
memset(buf, 0, sizeof(buf));
f.read((char*) buf, sizeof(buf));
for (int i = 0; i < 120; ++i)
hash ^= buf[i];
}
return hash;
}
|
|
|
#pragma once
#include <string>
#include <vector>
using std::string;
using std::vector;
struct FileSystem
{
static void CreateDir(const string& dir);
static bool DirExists(const string& dir);
typedef vector<string> PathList;
static PathList GetDirs(const string& dir);
static PathList GetFiles(const string& rootdir);
static bool FilesAreEqual(const string& masterdir, const string& slavedir, const string& fname);
static unsigned long GetHash(const string& path);
} ;
|
|
|
#include <fstream>
using namespace std;
#include <time.h>
#include "globals.h"
Globals globals;
Globals::Globals()
: Log("jContinuousBackup.log", ios::out | ios::app)
{
}
ostream& now(ostream& os)
{
time_t tt = time(0);
char buf[50];
strftime(buf, sizeof(buf), "%y/%m/%d.%H:%M:%S: ", localtime(&tt));
os << buf;
return os;
}
|
|
|
#pragma once
#include <ostream>
#include <fstream>
using std::ofstream;
using std::ostream;
using std::endl;
class Globals
{
public:
Globals();
ofstream Log;
} ;
ostream& now(ostream& os);
extern Globals globals;
|
|
|
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "globals.h"
#include "DirInfo.h"
#include "Notification.h"
#include "ConfigFile.h"
#include "CompareDirTrees.h"
class App : public Notification::Functor, public CompareDirTrees::Functor
{
public:
void Run()
{
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST);
Notification notify(*this);
ConfigFile cf(notify);
cf.Load();
//force a comparison first time through
notify.ForceComparison();
notify.DetectChanges();
notify.Term();
}
void operator() (const DirInfo& di)
{
CompareDirTrees cdt;
cdt.Compare(di.dir, di.backupdir, di.subdirs, *this);
}
void operator() (const string& srcdir, const string& destdir, const string& filename)
{
globals.Log << now
<< "Changed: dir=" << srcdir
<< " backupdir=" << destdir
<< " file=" << filename
<< endl;
::CopyFile(string(srcdir + "\\" + filename).c_str(), string(destdir + "\\" + filename).c_str(), 0);
}
} ;
int __stdcall WinMain(struct HINSTANCE__*, struct HINSTANCE__*, char*, int)
{
App app;
app.Run();
return 0;
}
|
|
|
#include <string>
using namespace std;
#include "Notification.h"
#include "NotificationHandles.h"
class Notification::Private
{
public:
Private(Functor& fn)
: mCallback(fn)
{
}
NotificationHandles mHandles;
Functor& mCallback;
} ;
Notification::Notification(Functor& fn)
: impl(* new Notification::Private(fn))
{
}
Notification::~Notification()
{
delete &impl;
}
void Notification::Add(const string& dir, bool subdirs, const string& backupdir)
{
impl.mHandles.Add(dir, subdirs, backupdir);
}
void Notification::ForceComparison()
{
for(impl.mHandles.Reset(); impl.mHandles.More(); )
impl.mCallback(impl.mHandles.Current());
}
void Notification::DetectChanges()
{
for(;;)
{
int index = impl.mHandles.WaitForChange();
if (impl.mHandles.IsExit(index))
return;
impl.mCallback(impl.mHandles.InfoAt(index));
impl.mHandles.Refresh();
}
}
void Notification::Term()
{
impl.mHandles.Term();
}
|
|
|
#pragma once
#include <string>
using std::string;
#include "DirInfo.h"
class Notification
{
public:
struct Functor
{
virtual void operator() (const DirInfo& di) = 0;
} ;
Notification(Functor& fn);
Notification::~Notification();
void Add(const string& dir, bool subdirs, const string& backupdir);
void ForceComparison();
void DetectChanges();
void Term();
private:
class Private;
Private& impl;
} ;
|
|
|
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <vector>
using namespace std;
#include "globals.h"
#include "NotificationHandles.h"
struct HandleInfo
{
HANDLE handle;
DirInfo dirinfo;
} ;
typedef vector<HandleInfo> Handles;
class NotificationHandles::Private
{
public:
Handles mHandles;
int mCurrentIndex;
} ;
NotificationHandles::NotificationHandles()
: impl(* new NotificationHandles::Private)
{
HandleInfo hi;
hi.dirinfo.dir = "exit";
hi.dirinfo.backupdir = "exit";
hi.dirinfo.subdirs = false;
hi.handle = CreateEvent(0, FALSE, FALSE, 0);
if (hi.handle == INVALID_HANDLE_VALUE)
{
globals.Log << now << "Bad handle on CreateEvent gle=" << GetLastError() << endl;
exit(0);
}
impl.mHandles.push_back(hi);
}
NotificationHandles::~NotificationHandles()
{
Term();
delete &impl;
}
bool NotificationHandles::IsExit(int i)
{
return i == 0;
}
DirInfo NotificationHandles::InfoAt(int i)
{
return impl.mHandles[i].dirinfo;
}
void NotificationHandles::Reset()
{
impl.mCurrentIndex = -1;
}
bool NotificationHandles::More()
{
for(impl.mCurrentIndex++; impl.mCurrentIndex < (int) impl.mHandles.size(); impl.mCurrentIndex++)
{
if (InfoAt(impl.mCurrentIndex).dir != "exit")
break;
}
return impl.mCurrentIndex < (int) impl.mHandles.size();
}
DirInfo NotificationHandles::Current()
{
return InfoAt(impl.mCurrentIndex);
}
void NotificationHandles::SignalToExit()
{
//TBD
}
void NotificationHandles::Add(const string& dir, bool subdirs, const string& backupdir)
{
HandleInfo hi;
hi.dirinfo.dir = dir;
hi.dirinfo.subdirs = subdirs;
hi.dirinfo.backupdir = backupdir;
hi.handle = FindFirstChangeNotification(
dir.c_str(), //path
subdirs ? 1 : 0, //watch sub tree
FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_NOTIFY_CHANGE_LAST_WRITE);
if (hi.handle == INVALID_HANDLE_VALUE)
{
globals.Log << now << "Bad handle on findfirstchange gle=" << GetLastError() << endl;
globals.Log << "dir=" << dir << endl;
return;
}
impl.mHandles.push_back(hi);
}
int NotificationHandles::WaitForChange()
{
HANDLE* handles = new HANDLE[impl.mHandles.size()];
int i = 0;
for(Handles::iterator it = impl.mHandles.begin(); it != impl.mHandles.end(); ++it)
{
handles[i] = (*it).handle;
i++;
}
DWORD rc = WaitForMultipleObjects(impl.mHandles.size(), handles, FALSE, INFINITE);
if (rc == WAIT_TIMEOUT)
throw "timeout occurred!";
DWORD index = rc - WAIT_OBJECT_0;
if (index < 0 || index >= impl.mHandles.size())
{
globals.Log << now << "error on WaitForMultiple index=" << index << " gle=" << GetLastError() << endl;
throw "error occurred";
}
//os << "Change detected: handle=" << index << endl;
return index;
}
void NotificationHandles::Refresh()
{
int i = 0;
for(Handles::iterator it = impl.mHandles.begin(); it != impl.mHandles.end(); ++it)
{
if (i != 0)
{
BOOL wrc = FindNextChangeNotification((*it).handle);
if (!wrc)
{
globals.Log << now << "FindNextChangeNotification failed: gle=" << GetLastError() << endl;
globals.Log << " dir=" << (*it).dirinfo.dir << endl;
}
}
i++;
}
}
void NotificationHandles::Term()
{
int i = 0;
for(Handles::iterator it = impl.mHandles.begin(); it != impl.mHandles.end(); ++it)
{
if (i == 0)
CloseHandle((*it).handle);
else
{
BOOL wrc = FindCloseChangeNotification((*it).handle);
if (!wrc)
{
globals.Log << now << "FindCloseChangeNotification failed: gle=" << GetLastError() << endl;
globals.Log << " dir=" << (*it).dirinfo.dir << endl;
}
}
i++;
}
}
//FILE_NOTIFY_CHANGE_FILE_NAME
//FILE_NOTIFY_CHANGE_DIR_NAME
//FILE_NOTIFY_CHANGE_ATTRIBUTES
//FILE_NOTIFY_CHANGE_SIZE
//FILE_NOTIFY_CHANGE_LAST_WRITE
//if a first-level subdir is changed, the '.' file is changed and
// so the Last_write is also changed
//but if a file is touched, this is the only one that triggers.
//FILE_NOTIFY_CHANGE_SECURITY
|
|
|
#pragma once
#include "DirInfo.h"
#include <string>
using std::string;
class NotificationHandles
{
public:
NotificationHandles();
~NotificationHandles();
bool IsExit(int i);
DirInfo InfoAt(int i);
void SignalToExit();
void Add(const string& dir, bool subdirs, const string& backupdir);
int WaitForChange();
void Refresh();
void Term();
void Reset();
bool More();
DirInfo Current();
private:
class Private;
Private& impl;
} ;
|