jMirror : mirrors one directory to another

Download jmirror.zip

Synopsis:

common.h
FileInfo.h
main.cpp
MirrorDir.cpp
MirrorDir.h
OsFileSearch.cpp
OsFileSearch.h
OsUtils.cpp
OsUtils.h
Statistics.h


common.h

Synopsis
#pragma once

//-----------------------------------------------------
template <class T, class Fn>
inline void for_all(const T& flist, Fn f)
  {
  std::for_each(flist.begin(), flist.end(), f);
  }

const int VERBOSE_QUIET = 0;
const int VERBOSE_NORMAL = 1;
const int VERBOSE_SOME = 2;
const int VERBOSE_VERY = 3;

extern int gVerbose; // 0 is quiet, 1 just the summary, 2 is some messages, 3 is very verbose
extern bool gDoCommands; // false to show commands but not execute them; true to execute commands

FileInfo.h

Synopsis
#pragma once

#include <string>
#include <vector>
#include <iostream>

struct FileInfo
  {
  FileInfo(const std::string& n, long sz)
    : name(n), size(sz)
    {
    }
  
  FileInfo(const FileInfo& other)
    : name(other.name), size(other.size)
    {
    }
  
  bool operator == (const FileInfo& other) const
    {
    return name == other.name;
    }
  
  const FileInfo& operator = (const FileInfo& other)
    {
    name = other.name;
    size = other.size;
    return *this;
    }
  std::string name;
  long size;
  } ;

typedef std::vector<FileInfo> FileInfoList;

//-----------------------------------------------------
inline bool IsFileLessThan(const FileInfo& left, const FileInfo& right)
  {
  return left.name < right.name;
  }

//-----------------------------------------------------
inline void WriteLine(const FileInfo& f)
  {
  if (gVerbose >= VERBOSE_SOME)
    std::cout << f.name << "\n";
  }

main.cpp

Synopsis

#include <iostream>
#include <string>
using std::cout;
using std::string;

#include "Statistics.h"
#include "OsUtils.h"
#include "MirrorDir.h"

Statistics gStats;
int gVerbose = VERBOSE_NORMAL; // 0 is quiet, 1 just the summary, 2 is some messages, 3 is very verbose
bool gDoCommands = true; // false to show commands but not execute them; true to execute commands

//-----------------------------------------------------
class App
  {
  public:
    void Run()
      {
      if (!OsUtils::DirExists(rightroot))
        OsUtils::mkdir(rightroot);
      MirrorDir(leftroot, rightroot);
      }
    
    //-----------------------------------------------------
    void usage()
      {
      cout << "usage: mirror leftdir rightdir [options]\n";
      cout << "   options:\n";
      cout << "   -?   help\n";
      cout << "   -h   help\n";
      cout << "   -v   verbose\n";
      cout << "   -vv  very verbose\n";
      cout << "   -q   quiet\n";
      cout << "   -s   show only, do not execute\n";
      exit(1);
      }
    
    //-----------------------------------------------------
    void usage(const string& msg)
      {
      cout << "mirror: " << msg << "\n\n";
      usage();
      }
    
    void Init()
      {
      gStats.Init();
      SetDefaults();
      }
    
    void SetDefaults()
      {
      gVerbose = VERBOSE_NORMAL; 
      gDoCommands = true; // false to show commands but not execute them; true to execute commands
      }
    
    void ParseCommandLine(int argc, char** argv)
      {
      for(int i = 1; i < argc; ++i)
        {
        string arg = argv[i];
        if (arg == "-?" || arg == "-h")
          usage();
        else if (arg == "-v")
          gVerbose = VERBOSE_SOME;
        else if (arg == "-vv")
          gVerbose = VERBOSE_VERY;
        else if (arg == "-q")
          gVerbose = VERBOSE_QUIET;
        else if (arg == "-s")
          gDoCommands = false;
        else if (arg[0] == '-')
          usage("unknown switch");
        else if (leftroot.empty())
          leftroot = arg;
        else if (rightroot.empty())
          rightroot = arg;
        else
          usage("unknown argument");
        }
      
      if (leftroot.empty() || rightroot.empty())
        usage("must specify both directories");
      
      if (leftroot == rightroot)
        {
        cout << "Left and right roots must be different\n";
        exit(1);
        }
      
      if (!OsUtils::DirExists(leftroot) || !OsUtils::IsADir(leftroot))
        {
        cout << "left root does not exist or is not a valid directory\n";
        exit(1);
        }
      
      if (OsUtils::DirExists(rightroot) && !OsUtils::IsADir(rightroot))
        {
        cout << "right root is not a valid directory\n";
        exit(1);
        }
      }
    
    
    string leftroot;
    string rightroot;
    
  } ;

//-----------------------------------------------------
int main(int argc, char** argv)
  {
  App app;
  app.Init();
  app.ParseCommandLine(argc, argv);
  app.Run();
  gStats.Dump(cout);
  return 0;
  }

MirrorDir.cpp

Synopsis
#include <string>
#include <algorithm>
#include <iostream>
using std::string;
using std::sort;
using std::cout;

#include "Common.h"
#include "MirrorDir.h"
#include "FileInfo.h"
#include "OsUtils.h"
#include "OsFileSearch.h"

//-----------------------------------------------------
struct ChangeHandler
  {
  virtual void operator() (const string& ldir, const string& rdir, const FileInfo& f) const = 0;
  } ;

//-----------------------------------------------------
struct ChangeHandler2
  {
  virtual void operator() (const string& ldir, const FileInfo& l, const string& rdir, const FileInfo& r) const = 0;
  } ;


//prototypes:
void ScanDirectory(const string& dir, FileInfoList& flist, FileInfoList& dlist);
void compare(const string& leftdir, const FileInfoList& left, 
             const string& rightdir, const FileInfoList& right, 
             const ChangeHandler& insfile, const ChangeHandler& delfile, const ChangeHandler2& eqfile);



//-----------------------------------------------------
struct InsertedDir : public ChangeHandler
  {
  void operator() (const string& /*ld*/, const string& rd, const FileInfo& f) const
    {
    //remove directory in the right directory
    string dir = rd + "\\" + f.name;
    if (gVerbose >= VERBOSE_SOME)
      cout << "dir : rmdir-> " << dir << "\n";
    if (gDoCommands)
      OsUtils::rmtree(dir);
    }
  } ;

//-----------------------------------------------------
struct DeletedDir : public ChangeHandler
  {
  void operator() (const string& /*ld*/, const string& rd, const FileInfo& f) const
    {
    
    string dir = rd + "\\" + f.name;
    if (gVerbose >= VERBOSE_SOME)
      cout << "dir : mkdir-> " << dir << "\n";
    if (gDoCommands)
      OsUtils::mkdir(dir);
    }
  } ;

//-----------------------------------------------------
struct EqualDir : public ChangeHandler2
  {
  void operator() (const string& ld, const FileInfo& l, const string& /*rd*/, const FileInfo& /*r*/) const
    {
    // do nothing if the directories are equal
    if (gVerbose >= VERBOSE_VERY)
      cout << "dir : eq-> " << ld << "\\" << l.name << "\n";
    }
  } ;

//-----------------------------------------------------
struct InsertedFile : public ChangeHandler
  {
  void operator() (const string& /*ld*/, const string& rd, const FileInfo& f) const
    {
    //delete old files in the right directory root (make a backup???)
    string rpath = rd + "\\" + f.name;
    if (gVerbose >= VERBOSE_SOME)
      cout << "file: old-> 'del " << rpath << "'\n";
    if (gDoCommands)
      OsUtils::delfile(rpath);
    }
  } ;

//-----------------------------------------------------
struct DeletedFile : public ChangeHandler
  {
  void operator() (const string& ld, const string& rd, const FileInfo& f) const
    {
    // copy new from the left to the right directory root
    string lpath = ld + "\\" + f.name;
    string rpath = rd + "\\" + f.name;
    if (gVerbose >= VERBOSE_SOME)
      cout << "file: new-> 'copy " << lpath << " " << rpath << "'\n";
    if (gDoCommands)
      OsUtils::copyfile(lpath, rpath);
    }
  } ;

//-----------------------------------------------------
struct EqualFile : public ChangeHandler2
  {
  void operator() (const string& ld, const FileInfo& l, const string& rd, const FileInfo& r) const
    {
    string lpath = ld + "\\" + l.name;
    string rpath = rd + "\\" + l.name;
    if (l.size == r.size)
      {
      if (gVerbose >= VERBOSE_VERY)
        cout << "file: eq -> no action " << lpath << "\n";
      }
    else
      {
      if (gVerbose >= VERBOSE_SOME)
        cout << "file: chg-> 'copy " << lpath << " " << rpath << "'\n";
      if (gDoCommands)
        OsUtils::copyfile(lpath, rpath);
      }
    }
  } ;


//-----------------------------------------------------
void MirrorDir(const string& leftdir, const string& rightdir)
  {
  FileInfoList leftflist;
  FileInfoList leftdlist;
  
  FileInfoList rightflist;
  FileInfoList rightdlist;
  
  ScanDirectory(leftdir, leftflist, leftdlist);
  ScanDirectory(rightdir, rightflist, rightdlist);
  
  sort(leftflist.begin(), leftflist.end(), IsFileLessThan);
  sort(rightflist.begin(), rightflist.end(), IsFileLessThan);
  sort(leftdlist.begin(), leftdlist.end(), IsFileLessThan);
  sort(rightdlist.begin(), rightdlist.end(), IsFileLessThan);
  
  if (gVerbose >= VERBOSE_VERY)
    {
    cout << "------------------\n";
    for_all(leftdlist, WriteLine);
    cout << "files---\n";
    for_all(leftflist, WriteLine);
    cout << "------------------\n";
    for_all(rightdlist, WriteLine);
    cout << "files---\n";
    for_all(rightflist, WriteLine);
    cout << "------------------\n";
    }
  
  compare(leftdir, leftdlist, rightdir, rightdlist, InsertedDir(), DeletedDir(), EqualDir());
  compare(leftdir, leftflist, rightdir, rightflist, InsertedFile(), DeletedFile(), EqualFile());
  
  for(FileInfoList::iterator it = leftdlist.begin(); it != leftdlist.end(); ++it)
    MirrorDir(leftdir + "\\" + (*it).name, rightdir + "\\" + (*it).name);
  }

//-----------------------------------------------------
void ScanDirectory(const string& dir, FileInfoList& flist, FileInfoList& dlist)
  {
  OsFileSearch osfs(dir);
  for (osfs.First(); osfs.IsMore(); osfs.Next())
    {
    if (osfs.IsDots())
      ;
    else if (osfs.IsDir())
      dlist.push_back(FileInfo(osfs.GetRelativePath(), 0));
    else
      flist.push_back(FileInfo(osfs.GetRelativePath(), osfs.GetSize()));
    }
  }

//-----------------------------------------------------
//-- assume files are sorted
void compare(const string& leftdir, const FileInfoList& left, 
             const string& rightdir, const FileInfoList& right, 
             const ChangeHandler& insfile, const ChangeHandler& delfile, const ChangeHandler2& eqfile)
  {
  FileInfoList::const_iterator ileft = left.begin();
  FileInfoList::const_iterator iright = right.begin();
  
  while (ileft != left.end() || iright != right.end())
    {
    if (ileft == left.end())
      {
      insfile(leftdir, rightdir, *iright);
      ++iright;
      }
    else if (iright == right.end())
      {
      delfile(leftdir, rightdir, *ileft);
      ++ileft;
      }
    else if (*ileft == *iright)
      {
      eqfile(leftdir, *ileft, rightdir, *iright);
      ++ileft;
      ++iright;
      }
    
    //else if (IsPathLessThan((*ileft).name, (*iright).name))
    else if (IsFileLessThan(*ileft, *iright))
      {
      delfile(leftdir, rightdir, *ileft);
      ++ileft;
      }
    else
      {
      insfile(leftdir, rightdir, *iright);
      ++iright;
      }
    }
  }


MirrorDir.h

Synopsis
#pragma once
#include <string>

void MirrorDir(const std::string& leftdir, const std::string& rightdir);

OsFileSearch.cpp

Synopsis
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#include <string>
using std::string;

#include "OsFileSearch.h"

class OsFileSearch::Private
  {
  public:
    Private(const string& dir)
      : mRc(FALSE), hfile(INVALID_HANDLE_VALUE), mDir(dir)
      {
      }
    
    BOOL mRc;
    WIN32_FIND_DATA fdata;
    HANDLE hfile;
    const string& mDir;
  } ;

OsFileSearch::OsFileSearch(const string& dir)
: impl(* new OsFileSearch::Private(dir) )
  {
  }
OsFileSearch::~OsFileSearch()
  {
  Close();
  delete &impl;
  }
void OsFileSearch::First()
  {
  string fspec = impl.mDir + "\\*.*";
  impl.hfile = FindFirstFile(fspec.c_str(), &impl.fdata);
  impl.mRc = IsValid() ? TRUE : FALSE;
  }

bool OsFileSearch::IsMore()
  {
  return impl.mRc == TRUE;
  }
void OsFileSearch::Next()
  {
  impl.mRc =  FindNextFile(impl.hfile, &impl.fdata);
  }

bool OsFileSearch::IsDir()
  {
  return (impl.fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
  }
bool OsFileSearch::IsReadOnly()
  {
  return (impl.fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0;
  }

bool OsFileSearch::IsDots()
  {
  return strcmp(impl.fdata.cFileName, ".") == 0 || strcmp(impl.fdata.cFileName, "..") == 0;
  }
string OsFileSearch::GetFullPath()
  {
  return string(impl.mDir + "\\" + strlwr(impl.fdata.cFileName));
  }
string OsFileSearch::GetRelativePath()
  {
  return string(strlwr(impl.fdata.cFileName));
  }

long OsFileSearch::GetSize()
  {
  return impl.fdata.nFileSizeLow;
  }
void OsFileSearch::Close()
  {
  if (!IsValid()) return;
  FindClose(impl.hfile);
  impl.hfile = INVALID_HANDLE_VALUE;
  }

bool OsFileSearch::IsValid()
  {
  return impl.hfile != INVALID_HANDLE_VALUE;
  }

OsFileSearch.h

Synopsis
#pragma once

#include <string>
using std::string;

class OsFileSearch
  {
  public:
    explicit OsFileSearch(const string& dir);
    ~OsFileSearch();
    void First();
    bool IsMore();
    void Next();
    bool IsDir();
    bool IsReadOnly();
    bool IsDots();
    string GetFullPath();
    string GetRelativePath();
    long GetSize();
    void Close();
  private:
    bool IsValid();
    
  private:
    class Private;
    Private& impl;
  } ;


OsUtils.cpp

Synopsis
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#include <iostream>
using std::cout;

#include "OsUtils.h"
#include "Statistics.h"
#include "OsFileSearch.h"

//-----------------------------------------------------
bool OsUtils::DirExists(const string& d)
  {
  DWORD fattr = GetFileAttributes(d.c_str());
  if (fattr == 0xFFFFFFFF)
    {
    return false;
    }
  return true;
  }

//-----------------------------------------------------
bool OsUtils::IsADir(const string& d)
  {
  DWORD fattr = GetFileAttributes(d.c_str());
  if ((fattr & FILE_ATTRIBUTE_DIRECTORY) != 0) //is a directory
    {
    return true;
    }
  return false;
  }



void OsUtils::copyfile(const string& lpath, const string& rpath)
  {
  BOOL wrc = ::CopyFile(lpath.c_str(), rpath.c_str(), FALSE); 
  if (wrc) 
    {
    gStats.NumFilesCopied++;
    return;
    }
  gStats.NumFailedFiles++;
  CheckError("copy file failed");
  }
void OsUtils::delfile(const string& path)
  {
  BOOL wrc = ::DeleteFile(path.c_str());
  if (wrc) 
    {
    gStats.NumFilesDeleted++;
    return;
    }
  DWORD gle = GetLastError();
  if (gle == ERROR_ACCESS_DENIED)
    {
    ClearReadOnly(path);
    BOOL wrc = ::DeleteFile(path.c_str());
    if (wrc) 
      {
      gStats.NumFilesDeleted++;
      return;
      }
    }
  gStats.NumFailedFiles++;
  CheckError("delete file failed");
  }
void OsUtils::rmdir(const string& dir)
  {
  BOOL wrc = ::RemoveDirectory(dir.c_str()); 
  if (wrc) 
    {
    gStats.NumDirsDeleted++;
    return;
    }
  gStats.NumFailedDirs++;
  CheckError("remove directory failed");
  }
void OsUtils::mkdir(const string& dir)
  {
  BOOL wrc = ::CreateDirectory(dir.c_str(), 0);
  if (wrc) 
    {
    gStats.NumDirsCreated++;
    return;
    }
  gStats.NumFailedDirs++;
  CheckError("create directory failed");
  }
void OsUtils::rmtree(const string& dir)
  {
  OsFileSearch osfs(dir);
  for (osfs.First(); osfs.IsMore(); osfs.Next())
    {
    if (osfs.IsDots())
      ;
    else if (osfs.IsDir())
      rmtree(osfs.GetFullPath());
    else
      delfile(osfs.GetFullPath());
    }
  osfs.Close();
  OsUtils::rmdir(dir);
  }

void OsUtils::ClearReadOnly(const string& path)
  {
  DWORD attrs = ::GetFileAttributes(path.c_str());
  attrs = attrs & ~FILE_ATTRIBUTE_READONLY;
  ::SetFileAttributes(path.c_str(), attrs);
  }
void OsUtils::CheckError(const string& prefix)
  {
  DWORD gle = GetLastError();
  if (gle == ERROR_SHARING_VIOLATION)
    cout << prefix << ": (" << gle << ") SHARING_VIOLATION\n";
  else if (gle == ERROR_ACCESS_DENIED)
    cout << prefix << ": (" << gle << ") ACCESS_DENIED\n";
  else if (gle != 0)
    cout << prefix << ": " << gle << "\n";
  }


OsUtils.h

Synopsis
#pragma once

#include <string>
using std::string;

struct OsUtils
  {
  public:
    static void copyfile(const string& lpath, const string& rpath);
    static void delfile(const string& path);
    static void rmdir(const string& dir);
    static void mkdir(const string& dir);
    static void rmtree(const string& dir);
    static bool IsADir(const string& d);
    static bool DirExists(const string& d);
    
  private:
    static void ClearReadOnly(const string& path);
    static void CheckError(const string& prefix);
  } ;


Statistics.h

Synopsis
#pragma once

#include <ostream>
using std::ostream;

#include "Common.h"

struct Statistics
  {
  int NumDirsDeleted;
  int NumDirsCreated;
  int NumFilesDeleted;
  int NumFilesCopied;
  int NumFailedFiles;
  int NumFailedDirs;
  
  Statistics()
    {
    Init();
    }
  
  void Init()
    {
    NumDirsDeleted = 0;
    NumDirsCreated = 0;
    NumFilesDeleted = 0;
    NumFilesCopied = 0;
    NumFailedFiles = 0;
    NumFailedDirs = 0;
    }
  
  void Dump(ostream& os)
    {
    if (gVerbose < VERBOSE_NORMAL) return;
    os << "Num directories deleted: " << NumDirsDeleted << "\n";
    os << "Num directories created: " << NumDirsCreated << "\n";
    os << "Num failed directories : " << NumFailedDirs << "\n";
    os << "Num files deleted      : " << NumFilesDeleted << "\n";
    os << "Num files copied       : " << NumFilesCopied  << "\n";
    os << "Num failed files       : " << NumFailedFiles << "\n";
    }
  
  } ;

extern Statistics gStats;






Contact me about content on this page using john_web-at-arrizza-dot-com
For Web Master or site problems contact: webadmin-at-arrizza-dot-com
Copyright John Arrizza (c) 2001-2010