//$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 <dircomp-cfg.h>
#include <ygp-cfg.h>

#include <cstdio>
#include <clocale>

#if SYSTEM == UNIX
#  include <unistd.h>
#endif

#include <iomanip>
#include <iostream>

#include <string>
#include <stdexcept>

#ifdef HAVE_CGP
#  include <CORBA.h>
#  include "../CGP/CICompDir.h"
#endif

#include <YGP/Check.h>
#include <YGP/Trace.h>

#include <YGP/Log.h>
#include <YGP/File.h>
#include <YGP/ATStamp.h>
#include <YGP/DirSrch.h>
#include <YGP/RDirSrch.h>
#include <YGP/IVIOAppl.h>
#include <YGP/FileRExp.h>
#include <YGP/SmartPtr.h>
#include <YGP/RDirSrchSrv.h>

#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<char*> (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 ()
        << _(" [<OPTION> [...]] <Originaldir> <Comparedir>")
#ifdef ENABLE_SERVER
        << "\n    " << name () << " --daemon[[=]<port>]"
           "\n    " << name () << " --server[[=]<port>]"
#endif
#ifdef HAVE_CGP
        << "\n    " << name () << " --CORBA"
#endif
        << "\n\n" << _("  Originaldir ........... Original directory\n")
        << _("  Comparedir ............ Directory to compare (new)\n")
        << "  -s, --recursive ....... " << _("Compares subdirectories recursively\n")
        << "  -E, --rec-equal ....... " << _("Recurse into subdirectories found in both dirs\n")
        << "  -e, --no-changed ...... " << _("Suppress younger and older files\n")
        << "  -N, --existing ........ " << _("Suppress new and deleted files\n")
        << "  -e, --equal ........... " << _("Show equal files\n")
        << "  -d, --show-dirs ....... " << _("Displays directories with differnt timestamps\n")
        << "  -v, --verbose ......... " << _("Show files with timestamps\n")
        << "  -H, --hidden .......... " << _("Compare also hidden files\n")
        << "  -c, --compare ......... " << _("Compare contents of (differing) files (if readable)\n")
        << "  -C, --force-compare ... " << _("Compare contents of (all) files (if readable)\n")
        << "  -i, --include ......... " << _("<Filelist> Restricts the compare to passed files\n")
        << "  -x, --exclude ......... " << _("<Filelist>: Excludes the passed files\n")
        << "  -I, --include-dirs .... " << _("<Filelist>: Restricts the compare to passed dirs\n")
        << "  -X, --exclude-dirs .... " << _("<Filelist>: Excludes the passed directories\n")
        << "  -t, --begin-time ...... " << _("<Date>: Lower border of the time-range for compare\n")
        << "  -T, --end-time ........ " << _("<Date>: Upper border of the time-range for compare\n")
        << "  -V, --version ......... " << _("Output version information\n")
        << "  -q, --quiet ........... " << _("Quiet mode: Suppress any unneccessary output\n")
        << "  -Q, --silent .......... " << _("Silent mode: No output at all (for batch-processing)\n")
        << "  -a, --action .......... " << _("<Action>: Perform action on displayed files\n")
        << "  -A .................... " << _("<Change-ID>[<Action>]: Action for specified files\n")
#ifdef ENABLE_SERVER
        << "  -S, --server .......... " << _("[<port>]: Start as server (run in foreground)\n")
        << "  -D, --deamon .......... " << _("[<port>]: Start as server (run as deamon)\n")
#endif
        << "  -r, --ret-changed ..... " << _("Return number of changed files\n")
        << "  -R, --ret-new ......... " << _("Return number of new or deleted files\n")
#ifdef HAVE_CGP
        << "  --CORBA ................" << _("Start as CORBA-server; Write ID to stdout\n")
#endif
        << "  -h, -?, --help ........ " << _("Display help and exit\n\n"
           "Exit with the number of different files (unless specified differently)\n");

    if (option & SHOW_VERBOSE) {
      std::cout << _("\n<Date> is in the format D.M.YYYY-H:m:S (with any split-char; WITHOUT "
                "leading\n       zeros)"
                "\n\n<File> Filename, where the wildcards asterisk (*) for any"
                " number of any\n       characters and the questionmark (?) for"
                " any one character are supported.\n\n"
                "       Furthermore it is possible to specify valid characters"
                " in brackets ([)\n       and (]), either by listening all of"
                " them or by specifying the borders,\n       seperated with a"
                " minus (-). To invert this selection a leading caret (^)\n  "
                "     can be used.\n\n"
                "       Files are considered hidden, if either the appropiate "
                "flag of the\n       operating system ist set (OS/2, DOS/WINDOWS)"
                " or if they are starting\n       with a dot (.) (UNIX).\n\n"
                "<Filelist> List of <File>-nodes; seperated with the split-char"
                " of the \n\t   operating-system (the colon (:) in UNIX; the "
                "semicolon (;)\n\t   in OS/2 and DOS/Windows."
                "\n\n\t   These nodes are checked from right to left; if no "
                "include-option\n\t    (-i) is specified, a leading -i* is assumed.\n\n"
                "The options 'a' and 'A' specify, which actions shall be performed "
                "for the\ndiffering files (the option 'A' needs additionally a flag "
                "how the files\nshould differ (1: Younger, 2: Older, 4: Different, "
                "8: Equal, 16: Deleted,\n32: New; this values can be ORed together).\n\n"
                "Following placeholders are supported with the <Action>:\n\n"
                "   {OF}, {CF}: Name of original or compared directory (incl. relative path)\n"
                "   {OP}, {CP}: Relative path of original or compared directory\n\n"
                "Example.: \"xcopy {OF} {CP}\" -A40\n");
    } // endif
}

//-----------------------------------------------------------------------------
/// Performs the job of the application
/// \param argc: Number of arguments (without options)
/// \param argv: Array with arguments
/// \returns \c Number of different files
//-----------------------------------------------------------------------------
int Application::perform (int argc, const char* argv[]) {
   if ((argc) != 2) {
      showHelp ();
      return -1;
   }

   try {
      YGP::SmartPtr<YGP::IDirectorySearch> dirs[2];

      for (unsigned int i (0); i < (sizeof (dirs) / sizeof(dirs[0])); ++i) {
         try {
            dirs[i] = CompareDirs::makeSearchobject (argv[i], DEFAULTPORT);
	 }
	 catch (std::exception& e) {
            std::string error (_("-error accessing directory `%1'\nReason: %2"));
            error.replace (error.find ("%1"), 2, argv[i]);
            error.replace (error.find ("%2"), 2, e.what ());
            std::cerr << filename () << error << '\n';
            return -2;
         }
      }

      dc.setOriginalDir (*dirs[0]);
      dc.setCompareDir (*dirs[1]);
      dc.compare ();
   }
   catch (std::exception& e) {
      std::cerr << filename () << _("-error: ") << e.what () << '\n';
      return -3;
   }

   return (option & RET_CHANGED) ? dc.getChangedFiles () :
          (option & RET_NEWDEL) ? dc.getNewFiles () : dc.getDiffFiles ();
}


//-----------------------------------------------------------------------------
/// Entrypoint of application
/// \param argc: Number of parameters
/// \param argv: Array with pointer to parameter
/// \returns \c int: Status
//-----------------------------------------------------------------------------
int main (int argc, const char* argv[]) {

   Application appl (argc, argv);
   return appl.run ();
}


syntax highlighted by Code2HTML, v. 0.9.1