//$Id: CompDir.cpp,v 1.44 2007/03/08 19:20:53 markus Rel $
//PROJECT : DirComp
//SUBSYSTEM : CompDir
//REFERENCES :
//TODO :
//BUGS :
//REVISION : $Revision: 1.44 $
//AUTHOR : Markus Schwab
//CREATED : 29.7.1999
//COPYRIGHT : Copyright (C) 1999 - 2007
// 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; either version 2 of the License, or
// (at your option) any later version.
// 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifdef _MSC_VER
#pragma warning(disable:4786) // disable warning about truncating debug info
#endif
#include <cstdlib>
#include <limits.h>
#include <vector>
#include <fstream>
#include <dircomp-cfg.h>
#include <YGP/File.h>
#include <YGP/Trace.h>
#include <YGP/ATStamp.h>
#include <YGP/DirSrch.h>
#include <YGP/IDirSrch.h>
#include <YGP/RDirSrch.h>
#include <YGP/XDirSrch.h>
#include <YGP/PathSrch.h>
#include <YGP/FileRExp.h>
#include "CompDir.h"
static const unsigned int LEN_LINE = 512;
//-----------------------------------------------------------------------------
/// Defaultconstructor
//-----------------------------------------------------------------------------
CompareDirs::CompareDirs ()
: iOption (NO_OPTION), cOrigLen (0), datBegin (0), datEnd (time_t (INT_MAX))
, dirOrig (NULL), dirComp (NULL) {
TRACE9 ("CompareDirs::CompareDirs ()");
}
//-----------------------------------------------------------------------------
/// Copyconstructor
/// \param o: Object to copy
//-----------------------------------------------------------------------------
CompareDirs::CompareDirs (const CompareDirs& o)
: iOption (o.iOption) , cOrigLen (o.cOrigLen), files (o.files)
, dirs (o.dirs), datBegin (o.datBegin), datEnd (o.datEnd)
, dirOrig (o.dirOrig), dirComp (o.dirComp) {
TRACE9 ("CompareDirs::CompareDirs (const CompareDirs& o)");
}
//-----------------------------------------------------------------------------
/// Constructor
/// \param dirOrig: Original directory
/// \param dirOrig: Comparative directory
/// \param option: Options for compare
/// \pre Both strings are not empty
//-----------------------------------------------------------------------------
CompareDirs::CompareDirs (YGP::IDirectorySearch& dirOrig,
YGP::IDirectorySearch& dirComp, const int option)
: iOption (option), datBegin (0), datEnd (time_t (-1)) {
TRACE9 ("CompareDirs::CompareDirs (const std::string&, const std::string&, const int))");
Check1 (dirOrig.isValid ());
Check1 (dirComp.isValid ());
addIncludeDirs ("*");
setOriginalDir (dirOrig);
setCompareDir (dirComp);
}
//-----------------------------------------------------------------------------
/// Destructor
//-----------------------------------------------------------------------------
CompareDirs::~CompareDirs () {
TRACE9 ("CompareDirs::~CompareDirs ()");
}
//-----------------------------------------------------------------------------
/// Assignment-operator
/// \param o: Object to copy
/// \returns \c const CompareDirs&: *this
//-----------------------------------------------------------------------------
const CompareDirs& CompareDirs::operator= (const CompareDirs& o) {
TRACE9 ("CompareDirs::operator= (const CompareDirs&)");
if (&o != this) {
iOption = o.iOption;
dirOrig = o.dirOrig;
dirComp = o.dirComp;
cOrigLen = o.cOrigLen;
files = o.files;
dirs = o.dirs;
datBegin = o.datBegin;
datEnd = o.datEnd;
} // endif
return *this;
}
//-----------------------------------------------------------------------------
/// Sets the original-directory for the compare; remove trailing
/// directory-chars
/// \param dir: Original directory
//-----------------------------------------------------------------------------
void CompareDirs::setOriginalDir (YGP::IDirectorySearch& dir) {
TRACE8 ("CompareDirs::setOriginalDir (YGP::IDirectorySearch&) - "
<< dir.getSearchValue () << " (" << dir.isValid () << ')');
Check1 (dir.isValid ());
dirOrig = &dir;
cOrigLen = dirOrig->getSearchValue ().length () + 1; Check3 (cOrigLen);
}
//-----------------------------------------------------------------------------
/// Sets the comparative-directory for the compare; remove trailing
/// directory-chars
/// \param dir: Comparative directory
//-----------------------------------------------------------------------------
void CompareDirs::setCompareDir (YGP::IDirectorySearch& dir) {
TRACE8 ("CompareDirs::setComparelDir (YGP::IDirectorySearch&) - " << dir.getSearchValue ());
Check1 (dir.isValid ());
dirComp = &dir;
}
//-----------------------------------------------------------------------------
/// Converts the time from a const char* into a time_t
/// \param pTime: Pointer to characters forming time
/// \param time: time_t to create
/// \returns \c int: 0 OK
/// \pre pTime is ASCIIZ-string
//-----------------------------------------------------------------------------
int CompareDirs::setTime (const char* pTime, time_t& time) {
Check1 (pTime);
struct tm t;
memset (static_cast<char*> ((void*)&t), 0, sizeof (t));
TRACE3 ("CompareDirs::setTime (const char*, time_t&-> Set time " << pTime);
YGP::ATimestamp input (pTime);
if (!input.isDefined ())
return -1;
if (input.getYear () < 100) // Correct input according to struct tm
// TODO: FIXME?? Can that be the first year-2100-bug?
input.add (0, 0, input.getYear () < 80 ? 2000 : 1900);
time = input.toSysTime ();
return time == -1;
}
//-----------------------------------------------------------------------------
/// Checks the integrity of this object
/// \returns \c int: Status
/// - 0: OK
/// - 1: A path to compare is no directory
/// - 2: cOrigLen has no length
/// - 3: Invalid options
//-----------------------------------------------------------------------------
int CompareDirs::checkIntegrity () const {
TRACE1 ("CompareDirs::checkIntegrity () - " << ((dirOrig->isValid () || dirComp->isValid ())
? (cOrigLen
? (iOption & (OPT_SEARCHEQUALSUBDIRS | OPT_SEARCHSUBDIRS)
== (OPT_SEARCHEQUALSUBDIRS | OPT_SEARCHSUBDIRS) ? 3 : 0)
: 2) : 1));
return ((dirOrig->isValid () || dirComp->isValid ())
? (cOrigLen
? ((iOption & (OPT_SEARCHEQUALSUBDIRS | OPT_SEARCHSUBDIRS))
== (OPT_SEARCHEQUALSUBDIRS | OPT_SEARCHSUBDIRS) ? 3 : 0)
: 2) : 1);
}
//-----------------------------------------------------------------------------
/// Compares the files in two directories
/// \throw std::exception: Reported errors by search-classes
//-----------------------------------------------------------------------------
void CompareDirs::compare () throw (std::exception) {
Check1 (!checkIntegrity ());
TRACE1 ("CompareDirs::compare (statPath) - Handling path: "
<< dirOrig->getSearchValue ());
std::string strWork (dirOrig->getSearchValue ());
strWork += YGP::File::DIRSEPARATOR;
strWork += '*';
dirOrig->setSearchValue (strWork);
enterDirectory (dirOrig->getDirectory ());
std::vector <const YGP::File*> dirList;
const YGP::File* file;
unsigned long cOrigFiles (0);
unsigned long attribs (YGP::IDirectorySearch::FILE_NORMAL
| YGP::IDirectorySearch::FILE_READONLY
| YGP::IDirectorySearch::FILE_DIRECTORY);
if (iOption & OPT_CHECKHIDDEN)
attribs |= YGP::IDirectorySearch::FILE_HIDDEN;
// Get list of files in original dir and store in dirList
if ((file = dirOrig->find (attribs)))
do {
// Check if file matches in/exclude-criteria
if (file->isDirectory ()
? checkDirectory (file->name ())
: (YGP::_XDSfileIsValid (files, file->name ())
&& checkDate (file->time ()))) {
// Yes: Add found entry to filelist
TRACE3 ("CompareDirs::compare (statPath) - Originalfile: " << file->name ());
++cOrigFiles;
dirList.push_back (file->clone ());
} // endif
} while ((file = dirOrig->next ())); // end-do
// Check all files in strDirComp if they exist in strDirOrig
strWork = dirComp->getSearchValue ();
strWork += YGP::File::DIRSEPARATOR;
strWork += '*';
dirComp->setSearchValue (strWork);
enterDirectory (dirComp->getDirectory ());
file = dirComp->find (attribs);
while (file) {
// Check if file matches in/exclude-criteria
if (file->isDirectory ()
? !checkDirectory (file->name ())
: !(YGP::_XDSfileIsValid (files, file->name ()) && checkDate (file->time ()))) {
file = dirComp->next ();
continue;
} // endif
TRACE8 ("CompareDirs::compare (statPath) - Comparative file: " << file->name ());
// Check if found file exists also in original directory
std::vector<const YGP::File*>::iterator i;
for (i = dirList.begin (); i != dirList.end (); ++i, *i) {
if (*i && !file->compare (*i))
break;
} // endfor check all files in original dir
const YGP::File* pOrigEntry = NULL;
if (i != dirList.end ()) { // File found?
TRACE3 ("CompareDirs::compare (statPath) - Equal comparative file: "
<< file->name ());
pOrigEntry = *i;
if (!((iOption & OPT_NODIRCOMP) // No directory-compare wanted
&& file->isDirectory () // and (two) file(s) found and
&& pOrigEntry->isDirectory ())) {
long diff (pOrigEntry->time () - file->time ());
long diffContents ((iOption & OPT_IGNORETIMESTAMP) ? 1 : diff);
TRACE9 ("CompareDirs::compare (statPath) - Timestamps: "
<< pOrigEntry->time () << " <-> " << file->time ()
<< " -> " << diff);
if (diffContents // Timestamp different?
&& (iOption & OPT_CHECKCONTENTS) // Compare contents
&& (!(file->isDirectory () // (only for files)?
|| pOrigEntry->isDirectory ()))
&& (pOrigEntry->size () == file->size ())) // Same size?
if (!compareFiles (*pOrigEntry, *file, diffContents)) // Cont?
diffContents = diff;
if (diffContents) { // Timestamp still differnt
if (!(iOption & OPT_NOCHANGED))
showFile (((diff > 0) ? DIR_OLDER : diff ? DIR_YOUNGER : DIR_DIFFERENT),
pOrigEntry, file);
} // endif
else
if (iOption & OPT_EQUAL)
showFile (DIR_EQUAL, pOrigEntry, file);
} // endif files to compare
// Cleanup: Delete original file from search-list and - if
// only equal subdirs are to be compared - add this directory
--cOrigFiles;
dirList.erase (i); // Adapt list (clear found file)
if ((iOption & OPT_SEARCHEQUALSUBDIRS)
&& (pOrigEntry->isDirectory () && file->isDirectory ()))
dirList.push_back (pOrigEntry);
else
delete pOrigEntry;
} // endif file found
else
if (!(iOption & OPT_NONEWDEL))
showFile (DIR_NEW, NULL, file); // File not found -> show new
// If subdirs are compared and dir found: Store file (if orig no dir)
if (iOption & OPT_SEARCHSUBDIRS) {
if (file->isDirectory ())
dirList.push_back (file->clone ());
}
file = dirComp->next ();
} // end-while files available
std::vector<const YGP::File*>::iterator i (dirList.begin ());
// Show all files not found in strDirComp as deleted
while (cOrigFiles--) {
Check2 (i != dirList.end ());
file = *i; Check3 (file);
if (!(iOption & OPT_NONEWDEL))
showFile (DIR_DELETED, file, NULL);
if (!(file->isDirectory () && (iOption & OPT_SEARCHSUBDIRS))) {
*i = NULL;
delete file;
} // endif no dir and subdir-compare
++i;
} // end-while handle remaining original files
if (dirList.size ()) { // Compare subdirs (if there are one)
std::string strOrig (dirOrig->getDirectory ());
std::string strComp (dirComp->getDirectory ());
for (i = dirList.begin (); i != dirList.end (); ++i)
if ((*i) != NULL) {
file = *i;
TRACE8 ("Check subdir: " << file->name ());
Check3 (file->isDirectory ());
checkDirectory (file->name ());
std::string strTemp (file->name ());
strTemp += YGP::File::DIRSEPARATOR;
dirOrig->setSearchValue (strOrig + strTemp);
dirComp->setSearchValue (strComp + strTemp);
compare ();
delete file;
*i = NULL;
} // endif entry not NULL
dirOrig->setSearchValue (strOrig);
dirComp->setSearchValue (strComp);
} // endif compare subdirs
}
//-----------------------------------------------------------------------------
/// Compares the contents of two files
/// \param fileOrig: Name of original file
/// \param fileComp: Name of file to compare
/// \param cmp: Defines how fileOrig and fileComp differs (analogue to
/// \param strcmp); only valid if true is returned
/// \returns \c bool: Flag if compare was successful
/// \pre Both files must have the same length
//-----------------------------------------------------------------------------
bool CompareDirs::compareFiles (const YGP::File& fileOrig,
const YGP::File& fileComp, long& cmp) const {
char szLineOrig[LEN_LINE], szLineComp[LEN_LINE];
try {
cmp = 0;
// Open the files to compare
void* fOrig = fileOrig.open ("rb");
void* fComp = fileComp.open ("rb");
unsigned int len;
while (!fileOrig.isEOF (fOrig)) {
len = fileOrig.read (fOrig, szLineOrig, LEN_LINE);
fileComp.read (fComp, szLineComp, LEN_LINE);
cmp = memcmp (szLineOrig, szLineComp, len);
if (cmp)
break;
} // end-while not EOF
fileOrig.close (fOrig);
fileComp.close (fComp);
return true;
}
catch (YGP::FileError& e) {
TRACE1 ("CompareDirs::compareFiles (const YGP::File&, const YGP::File&, long&) -"
" Error during comparison: " << e.what ());
return false;
}
}
//-----------------------------------------------------------------------------
/// Checks if the passed pFile matches the passed dir-arguments
/// \param pFile: Directoryname
/// \returns \c bool: true: Directory matches; false else
/// \pre pFile valid ASCIIZ-string
//-----------------------------------------------------------------------------
bool CompareDirs::checkDirectory (const char* pFile) const {
Check1 (pFile); Check1 (*pFile);
// Ignore special-dirs '.' and '..'
if (!YGP::IDirectorySearch::isSpecial (pFile)) {
YGP::PathSearch l (dirs);
std::string node;
bool included (false), include (false);
TRACE7 ("CompareDirs::checkDirectory (const char*) - Checking " << pFile);
while (!(node = l.getNextNode ()).empty ()) {
Check2 ((node[0] == 'I') || (node[0] == 'X'));
include = (node[0] == 'I');
included |= include;
node.replace (0, 1, 0, '\0');
TRACE8 ("CompareDirs::checkDirectory (const char*) - Checking " << pFile
<< " against " << node);
if (checkDirname (pFile, node))
return include;
} // end-while nodes available
return included ? false : true;
} // endif
return false;
}
//-----------------------------------------------------------------------------
/// Checks if the passed pDir matches any of the nodes in the list
/// \param pDir: Directoryname
/// \param regexp: RegExp to check
/// \returns \c bool: true: Directory matches; false else
/// \pre pDir valid ASCIIZ-string
//-----------------------------------------------------------------------------
bool CompareDirs::checkDirname (const char* pDir,
const std::string& regexp) const {
Check1 (pDir); Check1 (*pDir); Check1 (!regexp.empty ());
std::string path (getDirStartComp ());
path += pDir;
YGP::FileRegularExpr rex (regexp.c_str ());
size_t pos;
do {
TRACE8 ("CompareDirs::checkDirname (const char*, std::string&) const - "
"Matching " << path << " against " << regexp);
if (rex.matches (path.c_str ())) // Test if dir matches
return true;
if ((pos = path.find (YGP::File::DIRSEPARATOR)) == std::string::npos)
break;
path.replace (0, pos + 1, 0, '\0');
} while (true);
return false;
}
//-----------------------------------------------------------------------------
/// Appends a node to a list. Every subnode in the node is prefixed with the
/// prefix-character
/// \param list: List to change
/// \param prefix: Leading character
/// \param node: Node to check
/// \param Notes : The node is added to the beginning of the list
//-----------------------------------------------------------------------------
void CompareDirs::addNode (std::string& list, char prefix, const std::string& node) {
YGP::PathSearch l (node);
std::string temp;
while (!(temp = l.getNextNode ()).empty ())
list = prefix + temp + (std::string)(YGP::PathSearch::PATHSEPARATOR + list);
}
//-----------------------------------------------------------------------------
/// Creates an object for comparing directories; depending on the passed
/// parameter local or over the net
/// \param search: Directory to search; if this parameter contains an
/// \param double-point (:) the part before is considered to be a host-name
/// \param (in Windoze of course only if it's longer than 1 char; fuck
/// \param those drive-letters)
/// \returns YGP::IDirectorySearch*: Pointer to search-object
/// \throw std::invalid_argument: In case of an error
//-----------------------------------------------------------------------------
YGP::IDirectorySearch* CompareDirs::makeSearchobject (const char* search,
unsigned int port) throw (std::invalid_argument) {
TRACE9 ("CompareDirs::makeSearchObject (const char*, unsigned int) - "
<< search << ':' << port);
Check1 (search); Check1 (*search);
std::string tmp (search);
size_t pos;
YGP::IDirectorySearch* ret (NULL);
#if SYSTEM == UNIX
if ((pos = tmp.find (YGP::RemoteDirSearch::SEPARATOR)) != std::string::npos)
#else
// Search after drive-letter-seperator in Windoze, ...
if ((pos = tmp.find (YGP::RemoteDirSearch::SEPARATOR, 2)) != std::string::npos)
#endif
{
unsigned int posPort (tmp.rfind (':'));
if (posPort > pos) {
TRACE8 ("CompareDirs::makeSearchObject (const char*) - Port = "
<< (search + posPort + 1));
port = YGP::Socket::getPortOfService (search + posPort + 1);
tmp.replace (posPort, tmp.length (), 0, '\0');
}
TRACE8 ("CompareDirs::makeSearchObject (const char*) - Server = "
<< tmp.c_str ());
ret = new YGP::RemoteDirSearch (tmp, port);
TRACE8 ("CompareDirs::makeSearchObject (const char*) - object created");
if (ret->isValid ())
errno = 0;
else {
errno = ENOENT;
delete ret;
}
}
else {
errno = 0;
if (YGP::DirectorySearch::isValid (tmp))
ret = new YGP::DirectorySearch (tmp);
}
if (errno)
throw (std::invalid_argument (strerror (errno)));
Check3 (ret);
return ret;
}
syntax highlighted by Code2HTML, v. 0.9.1