//$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 #include #include #include #include #include #include #include #include #include #include #include #include #include #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 ((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 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::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::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; }