//$Id: DirComp.cpp,v 1.53 2007/03/22 19:40:35 markus Rel $ //PROJECT : DirComp //SUBSYSTEM : DirComp //REFERENCES : //TODO : //BUGS : //REVISION : $Revision: 1.53 $ //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 #if SYSTEM == UNIX # include #endif #include #include #include #include #ifdef HAVE_CGP # include # include "../CGP/CICompDir.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include "CompDir.h" static const int DEFAULTPORT = 31336U; // Class to print result of CompareDirs class DirCompare2Cout : public CompareDirs { public: DirCompare2Cout (); virtual ~DirCompare2Cout () { } typedef enum Options { OPT_NONE = 0, OPT_NO_OUTPUT = 1, OPT_VERBOSE = 2 } Options; void setAction (unsigned int iDiffType, const char* pszAktion) { if (iDiffType < sizeof (pszActions) / sizeof (pszActions[0])) pszActions[iDiffType] = pszAktion; } int getDiffFiles () const { return iNumFiles; } int getNewFiles () const { return iNumNewFiles; } int getChangedFiles () const { return iNumChangedFiles; } void setOption (Options opt) { option = opt; } static unsigned int calcMaxStatiLen (); protected: virtual void showFile (const Status diff, const YGP::File* pFileOrig, const YGP::File* pFileComp = NULL); private: int iNumFiles; int iNumChangedFiles; int iNumNewFiles; Options option; const char* pszActions[DIR_NEW + 1]; // Array for actions void execProgram (const char* pszAction, const char* pszFile, const CompareDirs::Status diff) const; static char* apFileStati[]; static unsigned int maxStatiLen; }; // Class to run DirComp-Application class Application : public YGP::IVIOApplication { public: Application (const int argc, const char* argv[]) : YGP::IVIOApplication (argc, argv, lo), option (0) { } ~Application () { } protected: virtual bool handleOption (const char opt); // Program-handling virtual int perform (int argc, const char* argv[]); virtual const char* description () const { if (option & SHOW_VERSION) { static const std::string strLongVer = std::string (VERSION) + _(" - Compiled on ") + std::string (__DATE__ " - " __TIME__) + _("\nCopyright (C) 1999 - 2007 Markus Schwab; e-Mail: g17m0@lycos.com" "\nDistributed under the terms of the GNU General Public License"); return strLongVer.c_str (); } else return PRG_RELEASE; } // Help-handling virtual bool shallShowInfo () const { return !(option & NO_OUTPUT); } virtual void showHelp () const; private: // Prohobited manager functions Application (); Application (const Application&); const Application& operator= (const Application&); static const longOptions lo[]; DirCompare2Cout dc; enum { NO_OUTPUT = 1, SHOW_VERSION = 2, RET_CHANGED = 4, RET_NEWDEL = 8, SHOW_VERBOSE = 16 }; int option; }; #ifdef HAVE_GETTEXT // Initialize I18n *before* the first use of gettext! static int _x_ = (Application::initI18n (PACKAGE, LOCALEDIR), 0); #endif char* DirCompare2Cout::apFileStati[] = { _("Younger"), _("Older"), _("Diff"), _("Equal"), _("Deleted"), _("New") }; unsigned int DirCompare2Cout::maxStatiLen = DirCompare2Cout::calcMaxStatiLen (); const YGP::IVIOApplication::longOptions Application::lo[] = { { IVIOAPPL_HELP_OPTION }, #ifdef HAVE_CGP { "CORBA", '|' }, #endif #ifdef ENABLE_SERVER { "server", 'S' }, { "daemon", 'D' }, #endif { "ret-new", 'R' }, { "ret-changed", 'r' }, { "action", 'a' }, { "silent", 'Q' }, { "quiet", 'q' }, { "version", 'V' }, { "end-time", 'T' }, { "begin-time", 't' }, { "exclude-dirs", 'X' }, { "include-dirs", 'I' }, { "exclude", 'x' }, { "include", 'i' }, { "force-compare", 'C' }, { "compare", 'c' }, { "hidden", 'H' }, { "verbose", 'v' }, { "show-dirs", 'd' }, { "equal", 'e' }, { "existing", 'N' }, { "no-changed", 'n' }, { "rec-equal", 'E' }, { "recursive", 's' }, { NULL, '\0' } }; //----------------------------------------------------------------------------- /// Defaultconstructor //----------------------------------------------------------------------------- DirCompare2Cout::DirCompare2Cout () : CompareDirs (), iNumFiles (0) , iNumChangedFiles (0), iNumNewFiles (0), option (OPT_NONE) { addOption (CompareDirs::OPT_NODIRCOMP); Check2 ((sizeof (pszActions) / sizeof (pszActions[0])) == (DIR_NEW + 1)); for (unsigned int i (0); i < sizeof (pszActions) / sizeof (pszActions[0]); ++i) pszActions[i] = NULL; } //----------------------------------------------------------------------------- /// Displays the different file /// \param diff: Flag how the files differ /// \param pFileOrig: Original file (NULL if new) /// \param pFileComp: Compared file (NULL if deleted) //----------------------------------------------------------------------------- void DirCompare2Cout::showFile (const Status diff, const YGP::File* pFileOrig, const YGP::File* pFileComp) { Check1 (diff <= DIR_NEW); Check1 (((diff == DIR_NEW) ? !pFileOrig : pFileOrig != NULL)); Check1 (((diff == DIR_DELETED) ? !pFileComp : pFileComp != NULL)); // Adapt number of differnt files ++iNumFiles; if (diff < DIR_EQUAL) ++iNumChangedFiles; else if (diff > DIR_EQUAL) ++iNumNewFiles; if (!(option & OPT_NO_OUTPUT)) { if (diff == DIR_DELETED) // For easier handling adapt file- pFileComp = pFileOrig; // pointers the file was deleted Check3 (pFileComp); std::cout.setf (std::ios::left, std::ios::left|std::ios::right|std::ios::internal); std::cout << std::setw (maxStatiLen) << apFileStati[diff] << ": " << getDirStartComp () << pFileComp->name () << ' '; if (option & OPT_VERBOSE) { YGP::ATimestamp filetime (pFileComp->time (), false); Check3 (filetime.isDefined ()); std::string strTime (filetime.toString ()); std::cout.setf (std::ios::right, std::ios::left | std::ios::right | std::ios::internal); if ((maxStatiLen + getDirStartComp ().length () + strlen (pFileComp->name ())) <= (76 - ((diff < DIR_DELETED) ? (strTime.length () + 1) : 0))) std::cout << std::setw (76 - maxStatiLen - getDirStartComp ().length () - strlen (pFileComp->name ()) - ((diff < DIR_DELETED) ? (strTime.length () + 1) : 0)); std::cout << strTime.c_str (); if (diff < DIR_DELETED) { filetime.setGMT (pFileOrig->time ()); Check3 (filetime.isDefined ()); std::cout << ' ' << filetime.toString ().c_str (); } // endif file not deleted } // endif verbose mode std::cout << '\n'; } // endif output wanted // Perform the specified action if (pszActions[diff] && *pszActions[diff]) execProgram (pszActions[diff], pFileComp->name (), diff); } //----------------------------------------------------------------------------- /// Calculates the max. length of the change-stati-strings They can (and /// propably do) differ in every language /// \returns \c unsigned int: Length of longest stati-string //----------------------------------------------------------------------------- unsigned int DirCompare2Cout::calcMaxStatiLen () { for (unsigned int i (0); i < sizeof (apFileStati) / sizeof (apFileStati[0]); ++i) { Check2 (apFileStati[i]); unsigned int act (strlen (apFileStati[i])); if (act > maxStatiLen) maxStatiLen = act; } return maxStatiLen; } //----------------------------------------------------------------------------- /// Performs the passed action with the passed file Following subsitution is /// performed with the action {OP} ... replaced with the path of the original /// file {OF} ... replaced with path and name of the original file {CP} ... /// replaced with the path of the compared file {CF} ... replaced with path /// and name of the compared file /// \param pAction: Action to perform /// \param pFile: Paramter for substitution /// \param diff: Status of compare (new, deleted, ...) /// \pre pszAction, pszFile valid ASCIIZ-strings //----------------------------------------------------------------------------- void DirCompare2Cout::execProgram (const char* pAction, const char* pFile, const CompareDirs::Status diff) const { Check1 (pAction); Check1 (pFile); Check1 (diff <= DIR_NEW); TRACE8 ("DirCompare2Cout::execProgram -> Passed action: " << pAction); std::string strAction (pAction); Check3 (!strAction.empty ()); int iPos; static char* aPlaceholders[] = { "{OP}", "{OF}", "{CP}", "{CF}" }; // Substitute actual names in action for (unsigned int i (0); i < (sizeof (aPlaceholders) / sizeof (aPlaceholders[0])); ++i) { iPos = 0; while ((iPos = strAction.find (aPlaceholders[i], iPos)) != -1) { strAction.replace (iPos, 4, (i & 1) ? pFile : ""); strAction.replace (iPos, 0, (i & 2) ? getDirComp () : getDirOrig ()); } } // endfor; TRACE3 ("DirCompare2Cout::execProgram -> Final action: " << strAction); if (system (strAction.c_str ())) { std::string error (PACKAGE); error += _("-error: Can't execute action"); perror (error.c_str ()); } } //----------------------------------------------------------------------------- /// Handles the passed options /// \param option: Actual option /// \pre option not '\0' //----------------------------------------------------------------------------- bool Application::handleOption (const char opt) { Check1 (opt != '\0'); const char* pOptArg; switch (opt) { case 'I': case 'X': if ((pOptArg = getOptionValue ()) != NULL) { YGP::FileRegularExpr regexp (pOptArg); try { regexp.checkIntegrity ();; (opt == 'X') ? dc.addExcludeDirs (pOptArg) : dc.addIncludeDirs (pOptArg); } catch (std::invalid_argument& e) { std::string error (_("-Warning: Invalid name for a " "directory (%1).\nIgnoring option %2\n")); error.replace (error.find ("%1"), 2, e.what ()); error.replace (error.find ("%2"), 2, 1, opt); std::cerr << name () << error; } } else { std::string error (_("-Warning: Option %1 needs an argument!\n" "Ignoring option %1\n")); error.replace (error.find ("%1"), 2, 1, opt); error.replace (error.find ("%1"), 2, 1, opt); std::cerr << name () << error; } case 's': dc.addOption (CompareDirs::OPT_SEARCHSUBDIRS); if (dc.hasOption (CompareDirs::OPT_SEARCHEQUALSUBDIRS)) { std::cerr << name () << _("-Warning: Option 's' overwrites " "previous option 'E'\n"); dc.removeOption (CompareDirs::OPT_SEARCHEQUALSUBDIRS); } break; case 'E': dc.addOption (CompareDirs::OPT_SEARCHEQUALSUBDIRS); if (dc.hasOption (CompareDirs::OPT_SEARCHSUBDIRS)) { std::cerr << name () << _("-Warning: Option 'E' overwrites " "previous option 's'\n"); dc.removeOption (CompareDirs::OPT_SEARCHSUBDIRS); } break; case 'N': dc.addOption (CompareDirs::OPT_NONEWDEL); break; case 'n': dc.addOption (CompareDirs::OPT_NOCHANGED); break; case 'e': dc.addOption (CompareDirs::OPT_EQUAL); break; case 'C': dc.addOption (CompareDirs::OPT_IGNORETIMESTAMP); case 'c': dc.addOption (CompareDirs::OPT_CHECKCONTENTS); break; case 'd': dc.removeOption (CompareDirs::OPT_NODIRCOMP); break; case 'v': option |= SHOW_VERBOSE; dc.setOption (DirCompare2Cout::OPT_VERBOSE); break; case 'i': case 'x': if ((pOptArg = getOptionValue ()) != NULL) { YGP::FileRegularExpr regexp (pOptArg); try { regexp.checkIntegrity ();; (opt == 'x') ? dc.addExcludeFiles (pOptArg) : dc.addIncludeFiles (pOptArg); } catch (std::invalid_argument& e) { std::string error (_("-Warning: Invalid filename (%1).\n" "Ignoring option %2\n")); error.replace (error.find ("%1"), 2, e.what ()); error.replace (error.find ("%2"), 2, 1, opt); std::cerr << name () << error; } } else { std::string error (_("-Warning: Option %1 needs an argument!\n" "Ignoring option %1\n")); error.replace (error.find ("%1"), 2, 1, opt); error.replace (error.find ("%1"), 2, 1, opt); std::cerr << name () << error; } break; case 't': case 'T': if ((pOptArg = getOptionValue ()) != NULL) { if ((opt == 't') ? dc.setBeginTime (pOptArg) : dc.setEndTime (pOptArg)) { std::string err (_("-Warning: Date `%1' is not valid!\nIgnoring" " option %2\n")); err.replace (err.find ("%1"), 2, pOptArg); err.replace (err.find ("%2"), 2, 1, opt); std::cerr << name () << err; } } else { std::string error (_("-Warning: Option %1 needs an argument!\n" "Ignoring option %1\n")); error.replace (error.find ("%1"), 2, 1, opt); error.replace (error.find ("%1"), 2, 1, opt); std::cerr << name () << error; } break; case 'Q': dc.setOption (DirCompare2Cout::OPT_NO_OUTPUT); case 'q': option |= NO_OUTPUT; break; case 'V': option |= SHOW_VERSION; break; case 'H': dc.addOption (CompareDirs::OPT_CHECKHIDDEN); break; case 'r': case 'R': option |= (opt == 'r' ? RET_CHANGED : RET_NEWDEL); break; case 'a': case 'A': if ((pOptArg = getOptionValue ()) != NULL) { int iValue (0); if (opt == 'a') iValue = -1; else { while (isdigit (*pOptArg)) { iValue *= 10; iValue += (*pOptArg++) - '0'; } // end-while } if (iValue) { // Set action for all specified types for (unsigned int i (0); i < (sizeof (int) << 4); ++i) if (iValue & (1 << i)) dc.setAction (i, pOptArg); } else std::cerr << name () << _("-Warning: Option A needs a (numeric) " "change-value\n"); } // endif else { std::string error (_("-Warning: Option %1 needs an argument!\n" "Ignoring option %1\n")); error.replace (error.find ("%1"), 2, 1, opt); error.replace (error.find ("%1"), 2, 1, opt); std::cerr << name () << error; } break; #ifdef ENABLE_SERVER case 'S': case 'D': try { unsigned int port (DEFAULTPORT); if ((pOptArg = checkOptionValue ()) != NULL) { // Use another port getOptionValue (); port = YGP::Socket::getPortOfService (pOptArg); } TRACE1 ("DirComp: Listening on port " << port); YGP::Socket orig (port); if (opt == 'D') { pid_t pid (fork ()); switch (pid) { case 0: // Child-part: Perform compare in background close (0); close (1); close (2); break; case -1: throw std::runtime_error (_("Can't create background-process")); default: // Parent-part: Just exit exit (0); } // end-switch } // endif daemon-mode while (true) { TRACE5 ("DirComp: Waiting for input"); YGP::Socket sock (orig.waitForInput ()); YGP::RemoteDirSearchSrv rds; rds.performCommands (sock); } // end-while } catch (std::exception& e) { if (opt == 'D') { YGP::Syslog log (name ()); std::string error (_("Initialisation-error: ")); error += e.what (); LOGERROR (error.c_str ()); } std::cerr << name () << _("-Error during initialisation: ") << e.what () << '\n'; exit (1); } break; #endif #ifdef HAVE_CGP case '|': { int argc (1); char* argv[1] = { const_cast (filename ()) }; CORBA::ORB_var orb = CORBA::ORB_init (argc, argv, "mico-local-orb" ); CICompareDirs* compDirs = new CICompareDirs (); CORBA::String_var id = orb->object_to_string (compDirs); std::cout << id << '\n'; std::cout.flush (); orb->run (); CORBA::release (compDirs); exit (0); } break; #endif default: { std::string error (_("-Warning: Ignoring invalid option %1\n")); error.replace (error.find ("%1"), 2, 1, opt); std::cerr << name () << error; } } // end-case return true; } //----------------------------------------------------------------------------- /// Shows the help for the programm //----------------------------------------------------------------------------- void Application::showHelp () const { std::cout << _("Compares two directories\n\n" "Usage") << ":\n " << name () << _(" [