btarcmp.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       btarcmp.cc
00003 ///             Compare / diff tool to analyze Barry backup tarballs
00004 ///
00005 
00006 /*
00007     Copyright (C) 2012, Net Direct Inc. (http://www.netdirect.ca/)
00008 
00009     This program is free software; you can redistribute it and/or modify
00010     it under the terms of the GNU General Public License as published by
00011     the Free Software Foundation; either version 2 of the License, or
00012     (at your option) any later version.
00013 
00014     This program is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00017 
00018     See the GNU General Public License in the COPYING file at the
00019     root directory of this project for more details.
00020 */
00021 
00022 #include <barry/barry.h>
00023 #include <barry/barrybackup.h>
00024 
00025 #include <iostream>
00026 #include <iomanip>
00027 #include <tr1/memory>
00028 #include <string>
00029 #include <vector>
00030 #include <map>
00031 #include <algorithm>
00032 #include <stdexcept>
00033 
00034 #include "barrygetopt.h"
00035 #include "util.h"
00036 
00037 using namespace std;
00038 using namespace std::tr1;
00039 using namespace Barry;
00040 
00041 /*
00042 Still TODO: should have the ability to copy all differing records
00043         into another tarball, to function as a "patch", so
00044         user can write it to his device to update only differing
00045         records, or to store as a record of changes, etc.
00046 */
00047 void Usage()
00048 {
00049    int logical, major, minor;
00050    const char *Version = Barry::Version(logical, major, minor);
00051 
00052    cerr
00053    << "btarcmp - Compare Barry backup tarballs\n"
00054    << "      Copyright 2012, Net Direct Inc. (http://www.netdirect.ca/)\n"
00055    << "      Using: " << Version << "\n"
00056    << "\n"
00057    << " Usage:  btarcmp [options...] tarball_0 tarball_1\n"
00058    << "\n"
00059    << "   -b        Use brief filename output\n"
00060    << "   -d db     Specify a specific database to compare.  Can be used\n"
00061    << "             multiple times.  If not used at all, all databases are\n"
00062    << "             compared.\n"
00063    << "   -D db     Specify a database name to skip.  If both -d and -D are\n"
00064    << "             used for the same database name, it will be skipped.\n"
00065    << "   -h        This help\n"
00066    << "   -I cs     International charset for string conversions\n"
00067    << "             Valid values here are available with 'iconv --list'\n"
00068    << "   -P        Only compare records that can be parsed\n"
00069    << "             This is the same as specifying -d for each database\n"
00070    << "             listed with -S.\n"
00071    << "   -S        Show list of supported database parsers.  Use twice\n"
00072    << "             to show field names as well.\n"
00073    << "   -v        Show verbose diff output (twice to force hex output)\n"
00074    << "\n"
00075    << endl;
00076 }
00077 
00078 
00079 //////////////////////////////////////////////////////////////////////////////
00080 // Utility functions and functors
00081 
00082 bool DBDataCmp(const DBData &a, const DBData &b)
00083 {
00084         return a.GetUniqueId() < b.GetUniqueId();
00085 }
00086 
00087 bool UnknownCmp(const UnknownField &a, const UnknownField &b)
00088 {
00089         return a.type < b.type;
00090 }
00091 
00092 class DBDataIdCmp
00093 {
00094         uint32_t m_id;
00095 
00096 public:
00097         explicit DBDataIdCmp(uint32_t id)
00098                 : m_id(id)
00099         {
00100         }
00101 
00102         bool operator()(const DBData &data) const
00103         {
00104                 return data.GetUniqueId() == m_id;
00105         }
00106 };
00107 
00108 void ChecksumDBData(const DBData &data, bool include_ids, std::string &sum)
00109 {
00110         Barry::SHA_CTX m_ctx;
00111 
00112         SHA1_Init(&m_ctx);
00113 
00114         if( include_ids ) {
00115                 SHA1_Update(&m_ctx, data.GetDBName().c_str(),
00116                         data.GetDBName().size());
00117 
00118                 uint8_t recType = data.GetRecType();
00119                 SHA1_Update(&m_ctx, &recType, sizeof(recType));
00120 
00121                 uint32_t uniqueId = data.GetUniqueId();
00122                 SHA1_Update(&m_ctx, &uniqueId, sizeof(uniqueId));
00123         }
00124 
00125         int len = data.GetData().GetSize() - data.GetOffset();
00126         SHA1_Update(&m_ctx,
00127                 data.GetData().GetData() + data.GetOffset(), len);
00128 
00129         unsigned char sha1[SHA_DIGEST_LENGTH];
00130         SHA1_Final(sha1, &m_ctx);
00131 
00132         ostringstream oss;
00133         for( int i = 0; i < SHA_DIGEST_LENGTH; i++ ) {
00134                 oss << hex << setfill('0') << setw(2)
00135                         << (unsigned int) sha1[i];
00136         }
00137         sum = oss.str();
00138 }
00139 
00140 
00141 //////////////////////////////////////////////////////////////////////////////
00142 // Parsed Compare class
00143 
00144 class ParsedCompare
00145 {
00146 private:
00147         const DBData &m_one, &m_two;
00148         const IConverter *m_ic;
00149         bool m_known_record;
00150         std::string m_first_description;
00151 
00152 public:
00153         ParsedCompare(const DBData &one, const DBData &two,
00154                 const IConverter *ic = 0);
00155 
00156         bool CanParse() const { return m_known_record; }
00157         const std::string& GetDescription() const { return m_first_description;}
00158 
00159         /// Returns true if differing fields found and displayed.
00160         /// False if no differences found.
00161         bool ShowDifferingFields();
00162 };
00163 
00164 ParsedCompare::ParsedCompare(const DBData &one,
00165                                 const DBData &two,
00166                                 const IConverter *ic)
00167         : m_one(one)
00168         , m_two(two)
00169         , m_ic(ic)
00170         , m_known_record(false)
00171 {
00172 #undef HANDLE_PARSER
00173 #define HANDLE_PARSER(tname) \
00174         else if( tname::GetDBName() == one.GetDBName() ) { \
00175                 m_known_record = true; \
00176                 tname a; \
00177                 ParseDBData(m_one, a, m_ic); \
00178                 m_first_description = a.GetDescription(); \
00179         }
00180 
00181         if( one.GetDBName() != two.GetDBName() ) {
00182                 throw logic_error("Different database types in ParsedCompare ctor!");
00183         }
00184         // fall through and use the else's
00185         ALL_KNOWN_PARSER_TYPES
00186 }
00187 
00188 template <class RecordT>
00189 class FieldHandler
00190 {
00191 private:
00192         const RecordT &m_one, &m_two;
00193         mutable bool m_found_difference;
00194 
00195 public:
00196         FieldHandler(const RecordT &one, const RecordT &two)
00197                 : m_one(one)
00198                 , m_two(two)
00199                 , m_found_difference(false)
00200         {
00201         }
00202 
00203         bool Differing() const { return m_found_difference; }
00204 
00205         void operator()(EnumFieldBase<RecordT> *ep,
00206                 const FieldIdentity &id) const
00207         {
00208                 if( ep->GetValue(m_one) == ep->GetValue(m_two) )
00209                         return;
00210 
00211                 m_found_difference = true;
00212                 cout << "   " << id.Name << ":\n"
00213                         << "         tar[0] = "
00214                         << ep->GetName(ep->GetValue(m_one))
00215                         << " (" << ep->GetValue(m_one) << ")\n"
00216                         << "         tar[1] = "
00217                         << ep->GetName(ep->GetValue(m_two))
00218                         << " (" << ep->GetValue(m_two) << ")"
00219                         << endl;
00220         }
00221 
00222         void operator()(typename FieldHandle<RecordT>::PostalPointer pp,
00223                 const FieldIdentity &id) const
00224         {
00225                 const std::string
00226                         &a = m_one.*(pp.m_PostalAddress).*(pp.m_PostalField),
00227                         &b = m_two.*(pp.m_PostalAddress).*(pp.m_PostalField);
00228 
00229                 if( a == b )
00230                         return;
00231 
00232                 m_found_difference = true;
00233                 cout << "   " << id.Name << ":\n"
00234                         << "         tar[0] = '" << a << "'\n"
00235                         << "         tar[1] = '" << b << "'"
00236                         << endl;
00237         }
00238 
00239         void operator()(std::string RecordT::* mp, const FieldIdentity &id) const
00240         {
00241                 if( m_one.*mp == m_two.*mp )
00242                         return;
00243 
00244                 m_found_difference = true;
00245                 cout << "   " << id.Name << ":\n"
00246                         << "         tar[0] = '"
00247                         << Cr2LfWrapper(m_one.*mp) << "'\n"
00248                         << "         tar[1] = '"
00249                         << Cr2LfWrapper(m_two.*mp) << "'"
00250                         << endl;
00251         }
00252 
00253         void operator()(UnknownsType RecordT::* mp, const FieldIdentity &id) const
00254         {
00255                 UnknownsType a = m_one.*mp, b = m_two.*mp;
00256 
00257                 sort(a.begin(), a.end(), UnknownCmp);
00258                 sort(b.begin(), b.end(), UnknownCmp);
00259 
00260                 if( a == b )
00261                         return;
00262 
00263                 m_found_difference = true;
00264                 cout << "   " << id.Name << ":\n"
00265                         << "         tar[0] = '" << a << "'\n"
00266                         << "         tar[1] = '" << b << "'"
00267                         << endl;
00268         }
00269 
00270         template <class TypeT>
00271         void operator()(TypeT RecordT::* mp, const FieldIdentity &id) const
00272         {
00273                 if( m_one.*mp == m_two.*mp )
00274                         return;
00275 
00276                 m_found_difference = true;
00277                 cout << "   " << id.Name << ":\n"
00278                         << "         tar[0] = '" << m_one.*mp << "'\n"
00279                         << "         tar[1] = '" << m_two.*mp << "'"
00280                         << endl;
00281         }
00282 };
00283 
00284 template <class RecordT>
00285 bool DoParsedCompare(const RecordT &a, const RecordT &b)
00286 {
00287         FieldHandler<RecordT> handler(a, b);
00288         ForEachField(RecordT::GetFieldHandles(), handler);
00289         return handler.Differing();
00290 }
00291 
00292 /// Returns true if differing fields found and displayed.
00293 /// False if no differences found.
00294 bool ParsedCompare::ShowDifferingFields()
00295 {
00296 #undef HANDLE_PARSER
00297 #define HANDLE_PARSER(tname) \
00298         else if( tname::GetDBName() == m_one.GetDBName() ) { \
00299                 tname a, b; \
00300                 ParseDBData(m_one, a, m_ic); \
00301                 ParseDBData(m_two, b, m_ic); \
00302                 return DoParsedCompare<tname>(a, b); \
00303         }
00304 
00305         if( !m_known_record ) {
00306                 return false;
00307         }
00308 
00309         ALL_KNOWN_PARSER_TYPES
00310 
00311         else {
00312                 return false;
00313         }
00314 }
00315 
00316 
00317 //////////////////////////////////////////////////////////////////////////////
00318 // Main application class
00319 
00320 class App
00321 {
00322 public:
00323         typedef Barry::ConfigFile::DBListType           DBListType;
00324         typedef std::vector<Barry::DBData>              DBDataList;
00325         typedef std::map<std::string, DBDataList>       DatabaseMap;
00326 
00327 private:
00328         DBListType m_compare_list;
00329         DBListType m_skip_list;
00330         DBListType m_valid_list;        // this list is created during the
00331                                         // database name compare... it holds
00332                                         // all the names that exist in both
00333                                         // maps, in sorted order
00334         DatabaseMap m_tars[2];
00335         std::string m_tarpaths[2];      // full filename with path
00336         std::string m_tarfiles[2];      // just filename, no path; or brief mark
00337         auto_ptr<IConverter> m_ic;
00338 
00339         int m_main_return;              // 0 - success
00340                                         // 1 - low level error or logic error
00341                                         // 2 - databases lists not the same
00342                                         // 3 - a record was added or deleted
00343         bool m_verbose;
00344         bool m_always_hex;
00345         bool m_sort_on_load;            // if true, sort each database by
00346                                         // Unique ID after loading from tarball
00347         bool m_include_ids;             // if true, include DBData IDs in SHA1
00348 
00349         std::string m_last_dbname;
00350 
00351 public:
00352         App();
00353 
00354         void LoadTarballs();
00355         void CompareDatabaseNames();
00356         void CompareData();
00357         void Compare(const std::string &dbname);
00358         void Compare(const std::string &dbname,
00359                 const DBDataList &one, const DBDataList &two);
00360         void Compare(const DBData &one, const DBData &two);
00361 
00362         bool Alike(DBDataList::const_iterator b1, DBDataList::const_iterator b2,
00363                 DBDataList::const_iterator e1, DBDataList::const_iterator e2);
00364         void SearchCheck(DBDataList::const_iterator &b,
00365                 DBDataList::const_iterator &e, const DBDataList &opposite_list,
00366                 const std::string &action);
00367 
00368         void ShowRecordDiff(const DBData &one, const DBData &two,
00369                 ParsedCompare &pc);
00370         void DumpRecord(const DBData &data);
00371         void ShowDatabaseHeader(const std::string &dbname);
00372         void AddParsersToCompare();
00373 
00374         // returns true if any of the items in Outputs needs a probe
00375         int main(int argc, char *argv[]);
00376 };
00377 
00378 //////////////////////////////////////////////////////////////////////////////
00379 // Memory storage parser
00380 
00381 class StoreParser : public Barry::Parser
00382 {
00383         App::DatabaseMap &m_map;
00384 
00385 public:
00386         explicit StoreParser(App::DatabaseMap &map)
00387                 : m_map(map)
00388         {
00389         }
00390 
00391         virtual void ParseRecord(const DBData &data, const IConverter *ic)
00392         {
00393                 m_map[data.GetDBName()].push_back(data);
00394         }
00395 };
00396 
00397 
00398 //////////////////////////////////////////////////////////////////////////////
00399 // Misc helpers dependent on App
00400 
00401 bool IdExists(const App::DBDataList &list, uint32_t id)
00402 {
00403         return find_if(list.begin(), list.end(), DBDataIdCmp(id)) != list.end();
00404 }
00405 
00406 //////////////////////////////////////////////////////////////////////////////
00407 // Member function definitions
00408 
00409 App::App()
00410         : m_main_return(0)
00411         , m_verbose(false)
00412         , m_always_hex(false)
00413         , m_sort_on_load(true)
00414         , m_include_ids(true)
00415 {
00416 }
00417 
00418 void App::AddParsersToCompare()
00419 {
00420 #undef HANDLE_PARSER
00421 #define HANDLE_PARSER(tname) \
00422         m_compare_list.push_back(tname::GetDBName());
00423 
00424         ALL_KNOWN_PARSER_TYPES
00425 }
00426 
00427 void App::LoadTarballs()
00428 {
00429         for( int i = 0; i < 2; i++ ) {
00430                 // load data into memory
00431                 Restore builder(m_tarpaths[i]);
00432                 StoreParser parser(m_tars[i]);
00433 
00434                 Pipe pipe(builder);
00435                 pipe.PumpFile(parser, m_ic.get());
00436 
00437                 // sort each database's record data by UniqueId
00438                 for( DatabaseMap::iterator b = m_tars[i].begin();
00439                         b != m_tars[i].end();
00440                         ++b )
00441                 {
00442                         if( m_sort_on_load )
00443                                 sort(b->second.begin(), b->second.end(), DBDataCmp);
00444                 }
00445         }
00446 }
00447 
00448 void App::CompareDatabaseNames()
00449 {
00450         for( int i = 1; i >= 0; i-- ) {
00451                 int other = i == 0 ? 1 : 0;
00452 
00453                 DatabaseMap::const_iterator b = m_tars[i].begin(), match;
00454                 for( ; b != m_tars[i].end(); ++b ) {
00455                         match = m_tars[other].find(b->first);
00456                         if( match == m_tars[other].end() ) {
00457                                 cout << m_tarfiles[other] << ": has no database '" << b->first << "'" << endl;
00458                                 m_main_return = 2;
00459                         }
00460                         else {
00461                                 if( !m_valid_list.IsSelected(b->first) ) {
00462                                         m_valid_list.push_back(b->first);
00463                                 }
00464                         }
00465                 }
00466         }
00467 
00468         // sort the valid list
00469         sort(m_valid_list.begin(), m_valid_list.end());
00470 //      cout << m_valid_list.size() << " valid database names found." << endl;
00471 }
00472 
00473 void App::CompareData()
00474 {
00475         DBListType::const_iterator valid = m_valid_list.begin();
00476         for( ; valid != m_valid_list.end(); ++valid ) {
00477                 // if m_compare_list contains items, then only compare
00478                 // if this database is present in the list
00479                 if( m_compare_list.size() && !m_compare_list.IsSelected(*valid) )
00480                         continue;
00481 
00482                 // check if we should skip this database
00483                 if( m_skip_list.IsSelected(*valid) )
00484                         continue;
00485 
00486                 // all's well so far... compare!
00487                 Compare(*valid);
00488         }
00489 }
00490 
00491 void App::Compare(const std::string &dbname)
00492 {
00493         DatabaseMap::const_iterator tar[2];
00494         tar[0] = m_tars[0].find(dbname);
00495         tar[1] = m_tars[1].find(dbname);
00496 
00497         if( tar[0] == m_tars[0].end() || tar[1] == m_tars[1].end() )
00498                 throw logic_error("Comparing non-existant database!" + dbname);
00499 
00500         Compare(dbname, tar[0]->second, tar[1]->second);
00501 }
00502 
00503 void App::Compare(const std::string &dbname,
00504                         const DBDataList &one,
00505                         const DBDataList &two)
00506 {
00507         DBDataList::const_iterator
00508                 b1 = one.begin(), e1 = one.end(),       // begin/end for one
00509                 b2 = two.begin(), e2 = two.end(),       // begin/end for two
00510                 s1, s2;                                 // search markers
00511 
00512         // if IDs are alike, compare
00513         // if not alike, then for each b1 and b2, do:
00514         //      search for id in opposite list
00515         //      if id found in opposite list, we're done, leave for next match
00516         //      if id not found, then entry has either been deleted or added
00517         //
00518         // NOTE: this algorithm assumes that both one and two are sorted!
00519         while( b1 != e1 || b2 != e2 ) {
00520                 if( Alike(b1, b2, e1, e2 ) ) {
00521                         Compare(*b1, *b2);
00522                         ++b1;
00523                         ++b2;
00524                         continue;
00525                 }
00526                 else {
00527                         // SearchCheck increments iterators if needed
00528                         SearchCheck(b1, e1, two, "deleted");
00529                         SearchCheck(b2, e2, one, "added");
00530                 }
00531         }
00532 }
00533 
00534 void App::Compare(const DBData &one, const DBData &two)
00535 {
00536         // make sure one and two are of the same database, or throw
00537         if( one.GetDBName() != two.GetDBName() )
00538                 throw logic_error("Tried to compare records from different databases: " + one.GetDBName() + ", and " + two.GetDBName());
00539 
00540         // always compare the sums of the data first, and if match, done
00541         string sum1, sum2;
00542         ChecksumDBData(one, m_include_ids, sum1);
00543         ChecksumDBData(two, m_include_ids, sum2);
00544         if( sum1 == sum2 )
00545                 return; // done
00546 
00547         // records are different, print concise report
00548         ShowDatabaseHeader(one.GetDBName());
00549 
00550         // if different, check if there's a parser available for this data
00551         // if not, display that these records differ, dump verbose if
00552         // needed, and done
00553         ParsedCompare pc(one, two, m_ic.get());
00554         ShowRecordDiff(one, two, pc);
00555 }
00556 
00557 void App::ShowRecordDiff(const DBData &one,
00558                         const DBData &two,
00559                         ParsedCompare &pc)
00560 {
00561         if( !pc.CanParse() ) {
00562                 // if can't parse, print:
00563                 //    UniqueID: sizes (one vs. two), X bytes differ
00564                 // then the differing fields
00565                 cout << "  0x" << hex << one.GetUniqueId() << ": differs: "
00566                         << dec
00567                         << "sizes (" << one.GetData().GetSize()
00568                         << " vs. " << two.GetData().GetSize()
00569                         << "), SHA1 sums differ"
00570                         << endl;
00571         }
00572         else {
00573                 // otherwise, print:
00574                 //    UniqueID: sizes (one vs. two), (custom display name)
00575                 cout << "  0x" << hex << one.GetUniqueId() << ": differs: "
00576                         << dec
00577                         << "sizes (" << one.GetData().GetSize()
00578                         << " vs. " << two.GetData().GetSize()
00579                         << "), "
00580                         << pc.GetDescription()
00581                         << endl;
00582 
00583                 if( !pc.ShowDifferingFields() ) {
00584                         // no difference found...
00585                         cout << "No differences found in parsed records, but SHA1 sums differ." << endl;
00586                 }
00587         }
00588 
00589         // if verbose and parser is null, or if always_hex,
00590         // then display a (messy?) hex diff of the raw data
00591         if( (m_verbose && !pc.CanParse()) || m_always_hex ) {
00592                 cout << "   Hex diff of record:" << endl;
00593                 cout << Diff(one.GetData(), two.GetData()) << endl;
00594         }
00595 }
00596 
00597 bool App::Alike(DBDataList::const_iterator b1,
00598                 DBDataList::const_iterator b2,
00599                 DBDataList::const_iterator e1,
00600                 DBDataList::const_iterator e2)
00601 {
00602         if( b1 == e1 || b2 == e2 )
00603                 return false;
00604         return b1->GetUniqueId() == b2->GetUniqueId();
00605 }
00606 
00607 std::string GetDBDescription(const DBData &data, const IConverter *ic)
00608 {
00609         string desc;
00610 
00611         // try to parse it
00612 #undef HANDLE_PARSER
00613 #define HANDLE_PARSER(tname) \
00614         if( data.GetDBName() == tname::GetDBName() ) { \
00615                 tname rec; \
00616                 ParseDBData(data, rec, ic); \
00617                 return rec.GetDescription(); \
00618         }
00619 
00620         ALL_KNOWN_PARSER_TYPES
00621 
00622         return desc;
00623 }
00624 
00625 void App::SearchCheck(DBDataList::const_iterator &b,
00626                         DBDataList::const_iterator &e,
00627                         const DBDataList &opposite_list,
00628                         const std::string &action)
00629 {
00630         // nothing to do if we're at end of list
00631         if( b == e )
00632                 return;
00633 
00634         // if id is found in opposite list, we're done!
00635         // leave the iterator as-is for the next cycle's match
00636         if( IdExists(opposite_list, b->GetUniqueId()) )
00637                 return;
00638 
00639         // id not found, so set return value
00640         m_main_return = 3;
00641 
00642         // if id not found, then entry has either been deleted or added
00643         // (action says which one), and we need to display the diff
00644         // and advance the iterator
00645         ShowDatabaseHeader(b->GetDBName());
00646         cout << "  0x" << hex << b->GetUniqueId() << ": record has been "
00647                 << action << " in " << "tar[1]";
00648         string desc = GetDBDescription(*b, m_ic.get());
00649         if( desc.size() ) {
00650                 cout << ": " << desc << endl;
00651         }
00652         else {
00653                 cout << endl;
00654         }
00655         if( m_verbose ) {
00656                 DumpRecord(*b);
00657         }
00658 
00659         // advance!
00660         ++b;
00661 }
00662 
00663 void App::DumpRecord(const DBData &data)
00664 {
00665 #undef HANDLE_PARSER
00666 #define HANDLE_PARSER(tname) \
00667         if( data.GetDBName() == tname::GetDBName() ) { \
00668                 tname rec; \
00669                 ParseDBData(data, rec, m_ic.get()); \
00670                 cout << rec << endl; \
00671                 return; \
00672         }
00673 
00674         ALL_KNOWN_PARSER_TYPES
00675 
00676         // if we get here, it's not a known record, so just dump the hex
00677         cout << data.GetData() << endl;
00678 }
00679 
00680 void App::ShowDatabaseHeader(const std::string &dbname)
00681 {
00682         if( dbname != m_last_dbname ) {
00683                 m_last_dbname = dbname;
00684                 cout << "In database: " << dbname << endl;
00685 
00686         }
00687 }
00688 
00689 int App::main(int argc, char *argv[])
00690 {
00691         bool brief = false;
00692         bool show_parsers = false, show_fields = false;
00693         string iconvCharset;
00694 
00695         // process command line options
00696         for(;;) {
00697                 int cmd = getopt(argc, argv, "bd:D:hI:PSv");
00698                 if( cmd == -1 )
00699                         break;
00700 
00701                 switch( cmd )
00702                 {
00703                 case 'b':       // use brief output
00704                         brief = true;
00705                         break;
00706 
00707                 case 'd':       // database name to compare
00708                         m_compare_list.push_back(optarg);
00709                         break;
00710 
00711                 case 'D':       // skip database to compare
00712                         m_skip_list.push_back(optarg);
00713                         break;
00714 
00715                 case 'P':       // only compare parseable records
00716                         AddParsersToCompare();
00717                         break;
00718 
00719                 case 'S':       // show parsers and builders
00720                         if( show_parsers )
00721                                 show_fields = true;
00722                         else
00723                                 show_parsers = true;
00724                         break;
00725 
00726                 case 'I':       // international charset (iconv)
00727                         iconvCharset = optarg;
00728                         break;
00729 
00730                 case 'v':       // verbose
00731                         if( !m_verbose )
00732                                 m_verbose = true;
00733                         else
00734                                 m_always_hex = true;
00735                         break;
00736 
00737                 case 'h':       // help
00738                 default:
00739                         Usage();
00740                         return 0;
00741                 }
00742         }
00743 
00744         if( show_parsers ) {
00745                 ShowParsers(show_fields, false);
00746                 return 0;
00747         }
00748 
00749         if( (optind + 2) > argc ) {
00750                 Usage();
00751                 return 0;
00752         }
00753 
00754         // save the tarball filenames for later processing
00755         // start out assuming both arguments are simple, no path filenames
00756         m_tarpaths[0] = m_tarfiles[0] = argv[optind];
00757         m_tarpaths[1] = m_tarfiles[1] = argv[optind+1];
00758 
00759         if( brief ) {
00760                 // user wants brief markers... filenames must be huge! :-)
00761                 m_tarfiles[0] = "tar[0]";
00762                 m_tarfiles[1] = "tar[1]";
00763         }
00764         else {
00765                 // attempt to trim paths to filenames only
00766                 if( m_tarpaths[0].find('/') != string::npos )
00767                         m_tarfiles[0] = m_tarpaths[0].substr(m_tarpaths[0].rfind('/') + 1);
00768                 if( m_tarpaths[1].find('/') != string::npos )
00769                         m_tarfiles[1] = m_tarpaths[1].substr(m_tarpaths[1].rfind('/') + 1);
00770 
00771                 // double check... don't want both markers the same:
00772                 if( m_tarfiles[0] == m_tarfiles[1] ) {
00773                         // doh... back to where we started
00774                         m_tarfiles[0] = m_tarpaths[0];
00775                         m_tarfiles[1] = m_tarpaths[1];
00776                 }
00777         }
00778 
00779         // display key for user
00780         cout << "tar[0] = " << m_tarpaths[0] << endl;
00781         cout << "tar[1] = " << m_tarpaths[1] << endl;
00782 
00783         // initialize the Barry library
00784         Barry::Init(false);
00785 
00786         // create an IConverter object if needed
00787         if( iconvCharset.size() ) {
00788                 m_ic.reset( new IConverter(iconvCharset.c_str(), true) );
00789         }
00790 
00791         // load both tarballs into memory for easy comparisons
00792         LoadTarballs();
00793 
00794         // compare plain list of database names first
00795         CompareDatabaseNames();
00796 
00797         // compare the actual data
00798         CompareData();
00799 
00800         return m_main_return;
00801 }
00802 
00803 int main(int argc, char *argv[])
00804 {
00805         try {
00806                 App app;
00807                 return app.main(argc, argv);
00808         }
00809         catch( std::exception &e ) {
00810                 cerr << "Exception: " << e.what() << endl;
00811                 return 1;
00812         }
00813 }
00814