backup commit of RPM backend
authorJiri Srain <jsrain@suse.cz>
Tue, 20 Dec 2005 12:59:33 +0000 (12:59 +0000)
committerJiri Srain <jsrain@suse.cz>
Tue, 20 Dec 2005 12:59:33 +0000 (12:59 +0000)
14 files changed:
zypp/target/rpm/BinHeader.cc [new file with mode: 0644]
zypp/target/rpm/BinHeader.h [new file with mode: 0644]
zypp/target/rpm/BinHeaderCache.cc [new file with mode: 0644]
zypp/target/rpm/BinHeaderCache.h [new file with mode: 0644]
zypp/target/rpm/Makefile.am [new file with mode: 0644]
zypp/target/rpm/RpmDb.cc [new file with mode: 0644]
zypp/target/rpm/RpmDb.h [new file with mode: 0644]
zypp/target/rpm/RpmHeader.cc [new file with mode: 0644]
zypp/target/rpm/RpmHeader.h [new file with mode: 0644]
zypp/target/rpm/RpmHeaderCache.cc [new file with mode: 0644]
zypp/target/rpm/RpmHeaderCache.h [new file with mode: 0644]
zypp/target/rpm/librpm.h [new file with mode: 0644]
zypp/target/rpm/librpmDb.cc [new file with mode: 0644]
zypp/target/rpm/librpmDb.h [new file with mode: 0644]

diff --git a/zypp/target/rpm/BinHeader.cc b/zypp/target/rpm/BinHeader.cc
new file mode 100644 (file)
index 0000000..a82ba44
--- /dev/null
@@ -0,0 +1,352 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/BinHeader.cc
+ *
+*/
+#include "librpm.h"
+
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+
+#include "zypp/target/rpm/BinHeader.h"
+
+using namespace std;
+
+#undef Y2LOG
+#define Y2LOG "BinHeader"
+
+namespace zypp {
+  namespace target {
+    namespace rpm {
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //        CLASS NAME : BinHeader::intList
+      //
+      ///////////////////////////////////////////////////////////////////
+      
+      BinHeader::intList::intList()
+          : cnt( 0 ), val( 0 ), type( RPM_NULL_TYPE )
+      {}
+      
+      unsigned BinHeader::intList::set( void * val_r, tag cnt_r, tag type_r ) {
+        val = val_r;
+        cnt = val ? cnt_r : 0;
+        type = type_r;
+        return cnt;
+      }
+      
+      int BinHeader::intList::operator[]( const unsigned idx_r ) const {
+        if ( idx_r < cnt ) {
+          switch ( type ) {
+          case RPM_CHAR_TYPE:
+            return ((char*)val)[idx_r];
+          case RPM_INT8_TYPE:
+            return ((int_8*)val)[idx_r];
+          case RPM_INT16_TYPE:
+            return ((int_16*)val)[idx_r];
+          case RPM_INT32_TYPE:
+            return ((int_32*)val)[idx_r];
+          }
+        }
+        return 0;
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //        CLASS NAME : BinHeader::stringList
+      //
+      ///////////////////////////////////////////////////////////////////
+      
+      void BinHeader::stringList::clear() {
+        if ( val )
+          free( val );
+        val = 0;
+        cnt = 0;
+      }
+      
+      BinHeader::stringList::stringList()
+          : cnt( 0 ), val( 0 )
+      {}
+      
+      unsigned BinHeader::stringList::set( char ** val_r, tag cnt_r ) {
+        clear();
+        val = val_r;
+        cnt = val ? cnt_r : 0;
+        return cnt;
+      }
+      
+      std::string BinHeader::stringList::operator[]( const unsigned idx_r ) const {
+        return( idx_r < cnt ? val[idx_r] : "" );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //        CLASS NAME : BinHeader
+      //
+      ///////////////////////////////////////////////////////////////////
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : BinHeader::BinHeader
+      //        METHOD TYPE : Constructor
+      //
+      BinHeader::BinHeader( Header h_r )
+          : _h( h_r )
+      {
+        if ( _h ) {
+          ::headerLink( _h );
+        }
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : BinHeader::BinHeader
+      //        METHOD TYPE : Constructor
+      //
+      BinHeader::BinHeader( BinHeader::Ptr & rhs )
+      {
+        INT << "INJECT from " << rhs;
+        if ( ! (rhs && rhs->_h) ) {
+          _h = 0;
+        } else {
+          _h = rhs->_h;  // ::headerLink already done in rhs
+          rhs->_h = 0;
+        }
+        INT << ": " << *this << "   (" << rhs << ")" << endl;
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : BinHeader::~BinHeader
+      //        METHOD TYPE : Destructor
+      //
+      BinHeader::~BinHeader()
+      {
+        if ( _h ) {
+          ::headerFree( _h );
+        }
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : BinHeader::assertHeader
+      //        METHOD TYPE : void
+      //
+      bool BinHeader::assertHeader()
+      {
+        if ( !_h ) {
+          _h = ::headerNew();
+          if ( !_h ) {
+            INT << "OOPS: NULL HEADER created!" << endl;
+            return false;
+          }
+        }
+        return true;
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : BinHeader::has_tag
+      //        METHOD TYPE : bool
+      //
+      //        DESCRIPTION :
+      //
+      bool BinHeader::has_tag( tag tag_r ) const
+      {
+        return( !empty() && ::headerIsEntry( _h, tag_r ) );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : BinHeader::int_list
+      //        METHOD TYPE : unsigned
+      //
+      //        DESCRIPTION :
+      //
+      unsigned BinHeader::int_list( tag tag_r, intList & lst_r ) const
+      {
+        if ( !empty() ) {
+          int_32 type = 0;
+          int_32 cnt  = 0;
+          void * val  = 0;
+          ::headerGetEntry( _h, tag_r, &type, &val, &cnt );
+      
+          if ( val ) {
+            switch ( type ) {
+            case RPM_NULL_TYPE:
+              return lst_r.set( 0, 0, type );
+            case RPM_CHAR_TYPE:
+            case RPM_INT8_TYPE:
+            case RPM_INT16_TYPE:
+            case RPM_INT32_TYPE:
+              return lst_r.set( val, cnt, type );
+      
+            case RPM_STRING_ARRAY_TYPE:
+              free( val );
+              // fall through
+            default:
+              INT << "RPM_TAG MISSMATCH: RPM_INT32_TYPE " << tag_r << " got type " << type << endl;
+            }
+          }
+        }
+        return lst_r.set( 0, 0, RPM_NULL_TYPE );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : BinHeader::string_list
+      //        METHOD TYPE : unsigned
+      //
+      //        DESCRIPTION :
+      //
+      unsigned BinHeader::string_list( tag tag_r, stringList & lst_r ) const
+      {
+        if ( !empty() ) {
+          int_32 type = 0;
+          int_32 cnt  = 0;
+          void * val  = 0;
+          ::headerGetEntry( _h, tag_r, &type, &val, &cnt );
+      
+          if ( val ) {
+            switch ( type ) {
+            case RPM_NULL_TYPE:
+              return lst_r.set( 0, 0 );
+            case RPM_STRING_ARRAY_TYPE:
+              return lst_r.set( (char**)val, cnt );
+      
+            default:
+              INT << "RPM_TAG MISSMATCH: RPM_STRING_ARRAY_TYPE " << tag_r << " got type " << type << endl;
+            }
+          }
+        }
+        return lst_r.set( 0, 0 );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : BinHeader::int_val
+      //        METHOD TYPE : int
+      //
+      //        DESCRIPTION :
+      //
+      int BinHeader::int_val( tag tag_r ) const
+      {
+        if ( !empty() ) {
+          int_32 type = 0;
+          int_32 cnt  = 0;
+          void * val  = 0;
+          ::headerGetEntry( _h, tag_r, &type, &val, &cnt );
+      
+          if ( val ) {
+            switch ( type ) {
+            case RPM_NULL_TYPE:
+              return 0;
+            case RPM_CHAR_TYPE:
+              return *((char*)val);
+            case RPM_INT8_TYPE:
+              return *((int_8*)val);
+            case RPM_INT16_TYPE:
+              return *((int_16*)val);
+            case RPM_INT32_TYPE:
+              return *((int_32*)val);
+      
+            case RPM_STRING_ARRAY_TYPE:
+              free( val );
+              // fall through
+            default:
+              INT << "RPM_TAG MISSMATCH: RPM_INT32_TYPE " << tag_r << " got type " << type << endl;
+            }
+          }
+        }
+        return 0;
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : BinHeader::string_val
+      //        METHOD TYPE : std::string
+      //
+      //        DESCRIPTION :
+      //
+      std::string BinHeader::string_val( tag tag_r ) const
+      {
+        if ( !empty() ) {
+          int_32 type = 0;
+          int_32 cnt  = 0;
+          void * val  = 0;
+          ::headerGetEntry( _h, tag_r, &type, &val, &cnt );
+      
+          if ( val ) {
+            switch ( type ) {
+            case RPM_NULL_TYPE:
+              return "";
+            case RPM_STRING_TYPE:
+              return (char*)val;
+      
+            case RPM_STRING_ARRAY_TYPE:
+              free( val );
+              // fall through
+            default:
+              INT << "RPM_TAG MISSMATCH: RPM_STRING_TYPE " << tag_r << " got type " << type << endl;
+            }
+          }
+        }
+        return "";
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : BinHeader::stringList_val
+      //        METHOD TYPE : std::list<std::string>
+      //
+      //        DESCRIPTION :
+      //
+      std::list<std::string> BinHeader::stringList_val( tag tag_r ) const
+      {
+        std::list<std::string> ret;
+      
+        if ( !empty() ) {
+          stringList lines;
+          unsigned count = string_list( tag_r, lines );
+          for ( unsigned i = 0; i < count; ++i ) {
+            ret.push_back( lines[i] );
+          }
+        }
+        return ret;
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //      METHOD NAME : BinHeader::dumpOn
+      //      METHOD TYPE : ostream &
+      //
+      //      DESCRIPTION :
+      //
+      ostream & BinHeader::dumpOn( ostream & str ) const
+      {
+        ReferenceCounted::dumpOn( str );
+        return str << '{' << (void*)_h << '}';
+      }
+
+    } // namespace rpm
+  } // namespace target
+} // namespace zypp
diff --git a/zypp/target/rpm/BinHeader.h b/zypp/target/rpm/BinHeader.h
new file mode 100644 (file)
index 0000000..af675b5
--- /dev/null
@@ -0,0 +1,148 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/BinHeader.h
+ *
+*/
+#ifndef ZYPP_TARGET_RPM_BINHEADER_H
+#define ZYPP_TARGET_RPM_BINHEADER_H
+
+extern "C" {
+#include <stdint.h>
+}
+
+#include <iosfwd>
+#include <string>
+#include <list>
+
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/target/rpm/librpm.h"
+
+namespace zypp {
+  namespace target {
+    namespace rpm {
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       CLASS NAME : BinHeader
+      /**
+       *
+       **/
+      class BinHeader : public base::ReferenceCounted, private base::NonCopyable
+      {
+
+        public:
+      
+         typedef intrusive_ptr<BinHeader> Ptr;
+
+         typedef intrusive_ptr<const BinHeader> constPtr;
+
+          typedef int32_t tag;
+      
+          class intList;
+      
+          class stringList;
+      
+        private:
+      
+          Header _h;
+      
+          bool assertHeader();
+      
+        public:
+      
+          BinHeader( Header h_r = 0 );
+      
+          /**
+           * <B>Dangerous!<\B> This one takes the header out of rhs
+           * and leaves rhs empty.
+           **/
+          BinHeader( BinHeader::Ptr & rhs );
+      
+          virtual ~BinHeader();
+      
+        public:
+      
+          bool empty() const { return( _h == NULL ); }
+      
+          bool has_tag( tag tag_r ) const;
+      
+          unsigned int_list( tag tag_r, intList & lst_r ) const;
+      
+          unsigned string_list( tag tag_r, stringList & lst_r ) const;
+      
+          int int_val( tag tag_r ) const;
+      
+          std::string string_val( tag tag_r ) const;
+      
+        public:
+      
+          std::list<std::string> stringList_val( tag tag_r ) const;
+      
+        public:
+      
+          virtual std::ostream & dumpOn( std::ostream & str ) const;
+      };
+      
+      ///////////////////////////////////////////////////////////////////
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       CLASS NAME : BinHeader::intList
+      /**
+       *
+       **/
+      class BinHeader::intList {
+        intList            ( const intList & );
+        intList & operator=( const intList & );
+        private:
+          unsigned cnt;
+          void *   val;
+          tag      type;
+        private:
+          friend class BinHeader;
+          unsigned set( void * val_r, tag cnt_r, tag type_r );
+        public:
+          intList();
+          unsigned size() const { return cnt; }
+          int operator[]( const unsigned idx_r ) const;
+      };
+      
+      ///////////////////////////////////////////////////////////////////
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       CLASS NAME : BinHeader::stringList
+      /**
+       *
+       **/
+      class BinHeader::stringList {
+        stringList            ( const stringList & );
+        stringList & operator=( const stringList & );
+        private:
+          unsigned cnt;
+          char **  val;
+          void clear();
+        private:
+          friend class BinHeader;
+          unsigned set( char ** val_r, tag cnt_r );
+        public:
+          stringList();
+          ~stringList() { clear(); }
+          unsigned size() const { return cnt; }
+          std::string operator[]( const unsigned idx_r ) const;
+      };
+      
+      ///////////////////////////////////////////////////////////////////
+
+    } // namespace rpm
+  } // namespace target
+} // namespace zypp
+
+#endif // ZYPP_TARGET_RPM_BINHEADER_H
diff --git a/zypp/target/rpm/BinHeaderCache.cc b/zypp/target/rpm/BinHeaderCache.cc
new file mode 100644 (file)
index 0000000..b945c31
--- /dev/null
@@ -0,0 +1,494 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/BinHeaderCache.cc
+ *
+*/
+#include "librpm.h"
+
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/Date.h"
+#include "zypp/PathInfo.h"
+#include "zypp/base/String.h"
+
+#include "zypp/target/rpm/BinHeader.h"
+#include "zypp/target/rpm/BinHeaderCache.h"
+
+using namespace std;
+
+namespace zypp {
+  namespace target {
+    namespace rpm {
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       CLASS NAME : BinHeaderCache::Cache
+      /**
+       *
+       **/
+      class BinHeaderCache::Cache {
+      
+        friend std::ostream & operator<<( std::ostream & str, const Cache & obj );
+      
+        Cache & operator=( const Cache & );
+        Cache            ( const Cache & );
+      
+        private:
+      
+          FD_t  fd;
+          pos   _fdpos;    // keep track of filepos for tell()
+      
+        public:
+      
+          Cache() : fd( 0 ), _fdpos( ~0 ) {}
+          ~Cache() { close(); }
+      
+        public:
+      
+          bool open( const Pathname & file_r );
+      
+          bool isOpen() const { return( fd != 0 ); }
+      
+          void close();
+      
+        public:
+      
+          pos tell() const;
+      
+          pos seek( const pos pos_r );
+      
+          unsigned readData( void * buf_r, unsigned count_r );
+      
+          Header readHeader( bool magicp = true );
+      };
+      
+      ///////////////////////////////////////////////////////////////////
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //       METHOD NAME : BinHeaderCache::Cache::open
+      //       METHOD TYPE : bool
+      //
+      bool BinHeaderCache::Cache::open( const Pathname & file_r )
+      {
+        close();
+      
+        switch ( zipType( file_r ) ) {
+        case filesystem::ZT_NONE:
+          {
+            fd = ::Fopen( file_r.asString().c_str(), "r.fdio"  );
+            DBG << "PLAIN: open 'r.fdio' " << fd << endl;
+          }
+          break;
+        case filesystem::ZT_GZ:
+          {
+            fd = ::Fopen( file_r.asString().c_str(), "r.gzdio"  );
+            DBG << "GZIP: open 'r.gzdio' " << fd << endl;
+          }
+          break;
+        case filesystem::ZT_BZ2:
+          {
+            ERR << "BZIP2 is not supported: " << file_r << endl;
+      #warning Check BZIP2 support
+            break;
+            fd = ::Fopen( file_r.asString().c_str(), "r.bzdio"  );
+            DBG << "BZIP2: open 'r.bzdio' " << fd << endl;
+          }
+          break;
+        }
+      
+        if ( fd == 0 || ::Ferror(fd) ) {
+          ERR << "Can't open cache for reading: " << file_r << " (" << ::Fstrerror(fd) << ")" << endl;
+          close();
+          return false;
+        }
+        _fdpos = 0;
+        return true;
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //       METHOD NAME : BinHeaderCache::Cache::close
+      //       METHOD TYPE : void
+      //
+      void BinHeaderCache::Cache::close()
+      {
+        if ( fd ) {
+          ::Fclose( fd );
+          fd = 0;
+          _fdpos = ~0;
+        }
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //       METHOD NAME : BinHeaderCache::Cache::tell
+      //       METHOD TYPE : pos
+      //
+      
+      extern "C" {
+        typedef struct X_FDSTACK_s {
+          FDIO_t       io;
+          void *       fp;
+          int          fdno;
+        } XFDSTACK_t;
+      
+        struct X_FD_s {
+          int          nrefs;
+          int          flags;
+          int          magic;
+      #define  XFDMAGIC        0x04463138
+          int          nfps;
+          XFDSTACK_t   fps[8];
+        };
+      }
+      
+      BinHeaderCache::pos BinHeaderCache::Cache::tell() const
+      {
+        pos rc = npos;
+      
+        struct X_FD_s * xfd = (struct X_FD_s*)fd;
+      
+        if ( !xfd || xfd->magic != XFDMAGIC) {
+          INT << "magic(" << XFDMAGIC << ") failed: " << xfd->magic << endl;
+          return rc;
+        }
+      
+        return _fdpos;
+      #if 0
+        if ( xfd->fps[xfd->nfps].io == fpio ) {
+          FILE * fp = (FILE *)xfd->fps[xfd->nfps].fp;
+          rc = ftell(fp);
+        }
+      
+        if ( rc == npos )
+          WAR << "Can't tell:" << ::Ferror(fd) << " (" << ::Fstrerror(fd) << ")" << endl;
+        return rc;
+      #endif
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //       METHOD NAME : BinHeaderCache::Cache::seek
+      //       METHOD TYPE : pos
+      //
+      BinHeaderCache::pos BinHeaderCache::Cache::seek( const pos pos_r )
+      {
+        pos rc = npos;
+      
+        if ( pos_r != npos ) {
+          ::Fseek( fd, pos_r, SEEK_SET );
+          _fdpos = pos_r;
+          if ( tell() == pos_r )
+            rc = pos_r;
+        } else {
+          INT << "Attempt to seek to pos -1" << endl;
+        }
+      
+        if ( rc == npos )
+          WAR << "Can't seek to " << pos_r << ":" << ::Ferror(fd) << " (" << ::Fstrerror(fd) << ")" << endl;
+        return rc;
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //       METHOD NAME : BinHeaderCache::Cache::readData
+      //       METHOD TYPE : unsigned
+      //
+      unsigned BinHeaderCache::Cache::readData( void * buf_r, unsigned count_r )
+      {
+        if ( !buf_r ) {
+          INT << "Attempt to fill NULL buffer" << endl;
+          return 0;
+        }
+        if ( !count_r ) {
+          return 0;
+        }
+      
+        unsigned got = ::Fread( buf_r, sizeof(char), count_r, fd );
+        _fdpos += got;
+        if ( got != count_r ) {
+          if ( got || ::Ferror(fd) ) {
+            ERR << "Error reading " << count_r << " byte (" << ::Fstrerror(fd) << ")" << endl;
+          } // else EOF?
+          return 0;
+        }
+        return count_r;
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //       METHOD NAME : BinHeaderCache::Cache::readHeader
+      //       METHOD TYPE : Header
+      //
+      
+      extern "C" {
+      #include <netinet/in.h>
+        // from rpm: lib/header.c
+        struct XXentryInfo {
+          int_32 tag;
+          int_32 type;
+          int_32 offset;              /* Offset from beginning of data segment,
+                                only defined on disk */
+          int_32 count;
+        };
+      }
+      
+      Header BinHeaderCache::Cache::readHeader( bool magicp )
+      {
+        static const int_32 rpm_header_magic = 0x01e8ad8e;
+      
+        int_32 block[4];
+        int_32 il, dl;
+        unsigned totalSize = 0;
+      
+        unsigned count = (magicp ? 4 : 2) * sizeof(int_32);
+        if ( readData( block, count ) != count ) {
+          ERR << "Error reading header info (" << ::Fstrerror(fd) << ")" << endl;
+          return 0;
+        }
+      
+        count = 0;
+      
+        if ( magicp ) {
+          if ( block[count] != rpm_header_magic ) {
+            ERR << "Error bad header magic " << str::hexstring( block[count] )
+       << " (" << str::hexstring( rpm_header_magic ) << ")" << endl;
+            return 0;
+          }
+          count += 2;
+        }
+      
+        il = ntohl( block[count++] );
+        dl = ntohl( block[count++] );
+      
+        totalSize = (2*sizeof(int_32)) + (il * sizeof(struct XXentryInfo)) + dl;
+        if (totalSize > (32*1024*1024)) {
+          ERR << "Error header ecxeeds 32Mb limit (" << totalSize << ")" << endl;
+          return NULL;
+        }
+      
+        char * data = new char[totalSize];
+        int_32 * p = (int_32 *)data;
+        Header h = 0;
+      
+        *p++ = htonl(il);
+        *p++ = htonl(dl);
+        totalSize -= (2*sizeof(int_32));
+      
+        if ( readData( p, totalSize ) != totalSize ) {
+          ERR << "Error reading header data (" << ::Fstrerror(fd) << ")" << endl;
+        } else {
+          h = ::headerCopyLoad( data );
+          if ( !h ) {
+            ERR << "Error loading header data" << endl;
+          }
+        }
+      
+        delete [] data;
+      
+        return h;
+      }
+      
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       CLASS NAME : BinHeaderCache
+      //
+      ///////////////////////////////////////////////////////////////////
+      
+      const unsigned BinHeaderCache::BHC_MAGIC_SZE( 64 );
+      
+      const BinHeaderCache::pos BinHeaderCache::npos( BinHeaderCache::pos(-1) );
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //       METHOD NAME : BinHeaderCache::BinHeaderCache
+      //       METHOD TYPE : Constructor
+      //
+      BinHeaderCache::BinHeaderCache( const Pathname & cache_r )
+          : _c( * new Cache )
+          , _cpath( cache_r )
+          , _cdate( 0 )
+          , _cheaderStart( npos )
+      {
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //       METHOD NAME : BinHeaderCache::~BinHeaderCache
+      //       METHOD TYPE : Destructor
+      //
+      BinHeaderCache::~BinHeaderCache()
+      {
+        delete &_c;
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //       METHOD NAME : BinHeaderCache::_cReadMagic
+      //       METHOD TYPE : int
+      //
+      //      [string\0][string\0][\0padded]
+      //
+      int BinHeaderCache::_cReadMagic()
+      {
+        char magic[BHC_MAGIC_SZE+1];
+        memset( magic, 0, BHC_MAGIC_SZE+1 );
+      
+        if ( _c.readData( magic, BHC_MAGIC_SZE ) != BHC_MAGIC_SZE ) {
+          ERR << "Error reading magic of cache file " << _cpath  << endl;
+          return -1;
+        }
+      
+        _cmagic = magic;
+        if ( _cmagic.size() < BHC_MAGIC_SZE ) {
+          _cdate = strtoul( magic+_cmagic.size()+1, 0, 10 );
+          if ( _cdate ) {
+            _cheaderStart = BHC_MAGIC_SZE;
+            return 0;
+          }
+        }
+      
+        ERR << "No magic in cache file " << _cpath  << endl;
+        return -2;
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //       METHOD NAME : BinHeaderCache::open
+      //       METHOD TYPE : bool
+      //
+      bool BinHeaderCache::open()
+      {
+        if ( _c.isOpen() )
+          return true;
+      
+        if ( !_c.open( _cpath ) ) {
+          close();
+          return false;
+        }
+      
+        if ( _cReadMagic() != 0 ) {
+          close();
+          return false;
+        }
+      
+        if ( !magicOk() ) {
+          ERR << "Bad magic in cache file " << _cpath  << endl;
+          close();
+          return false;
+        }
+      
+        return true;
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //       METHOD NAME : BinHeaderCache::isOpen
+      //       METHOD TYPE : bool
+      //
+      bool BinHeaderCache::isOpen() const
+      {
+        return _c.isOpen();
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //       METHOD NAME : BinHeaderCache::close
+      //       METHOD TYPE : void
+      //
+      void BinHeaderCache::close()
+      {
+        _cmagic       = "";
+        _cdate        = 0;
+        _cheaderStart = npos;
+        _c.close();
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //       METHOD NAME : BinHeaderCache::tell
+      //       METHOD TYPE : BinHeaderCache::pos
+      //
+      BinHeaderCache::pos BinHeaderCache::tell() const
+      {
+        return _c.tell();
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //       METHOD NAME : BinHeaderCache::seek
+      //       METHOD TYPE : BinHeaderCache::pos
+      //
+      BinHeaderCache::pos BinHeaderCache::seek( const pos pos_r )
+      {
+        return _c.seek( pos_r );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //       METHOD NAME : BinHeaderCache::readData
+      //       METHOD TYPE : unsigned
+      //
+      unsigned BinHeaderCache::readData( void * buf_r, unsigned count_r )
+      {
+        return _c.readData( buf_r, count_r );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //       METHOD NAME : BinHeaderCache::readHeader
+      //       METHOD TYPE : BinHeaderPtr
+      //
+      BinHeader::Ptr BinHeaderCache::readHeader( bool magicp )
+      {
+        Header h = _c.readHeader( magicp );
+        if ( !h )
+          return 0;
+        return new BinHeader( h );
+      }
+      
+      /******************************************************************
+      **
+      **
+      **       FUNCTION NAME : operator<<
+      **       FUNCTION TYPE : ostream &
+      */
+      ostream & operator<<( ostream & str, const BinHeaderCache & obj )
+      {
+        str << "BinHeaderCache@" << (void *)&obj;
+        if ( obj.isOpen() ) {
+          str << '(' << obj._cmagic << '|' << Date(obj._cdate) << "|at " << obj._cheaderStart << ')';
+        } else {
+          str << "(closed)";
+        }
+        return str;
+      }
+
+    } // namespace rpm
+  } // namespace target
+} // namespace zypp
diff --git a/zypp/target/rpm/BinHeaderCache.h b/zypp/target/rpm/BinHeaderCache.h
new file mode 100644 (file)
index 0000000..744b184
--- /dev/null
@@ -0,0 +1,107 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/BinHeaderCache.h
+ *
+*/
+#ifndef ZYPP_TARGET_BINHEADERCACHE_H
+#define ZYPP_TARGET_BINHEADERCACHE_H
+
+#include <iosfwd>
+
+#include "zypp/Pathname.h"
+
+#include "zypp/target/rpm/BinHeader.h"
+
+namespace zypp {
+  namespace target {
+    namespace rpm {
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       CLASS NAME : BinHeaderCache
+      /**
+       *
+       **/
+      class BinHeaderCache {
+      
+        friend std::ostream & operator<<( std::ostream & str, const BinHeaderCache & obj );
+      
+        BinHeaderCache & operator=( const BinHeaderCache & );
+        BinHeaderCache            ( const BinHeaderCache & );
+      
+        public:
+      
+          typedef unsigned pos;
+      
+          static const pos npos;
+      
+        private:
+      
+          static const unsigned BHC_MAGIC_SZE;
+      
+          class Cache;
+      
+          Cache & _c;
+      
+        private:
+      
+          int _cReadMagic();
+      
+        protected:
+      
+          Pathname _cpath;
+      
+        protected:
+      
+          std::string _cmagic;
+      
+          time_t _cdate;
+      
+          pos _cheaderStart;
+      
+        protected:
+      
+          virtual bool magicOk() { return _cmagic.empty(); }
+      
+        public:
+      
+          BinHeaderCache( const Pathname & cache_r );
+      
+          virtual ~BinHeaderCache();
+      
+        public:
+      
+          bool open();
+      
+          void close();
+      
+          bool isOpen() const;
+      
+          const Pathname & cpath() const { return  _cpath; }
+      
+          const std::string & cmagic() const { return _cmagic; }
+      
+          time_t cdate() const { return _cdate; }
+      
+          pos tell() const;
+      
+          pos seek( const pos pos_r );
+      
+          unsigned readData( void * buf_r, unsigned count_r );
+      
+          BinHeader::Ptr readHeader( bool magicp = true );
+      };
+      
+      ///////////////////////////////////////////////////////////////////
+
+    } // namespace rpm
+  } // namespace target
+} // namespace zypp
+
+#endif // ZYPP_TARGET_BINHEADERCACHE_H
diff --git a/zypp/target/rpm/Makefile.am b/zypp/target/rpm/Makefile.am
new file mode 100644 (file)
index 0000000..795f6e1
--- /dev/null
@@ -0,0 +1,33 @@
+## Process this file with automake to produce Makefile.in
+## ##################################################
+
+SUBDIRS = 
+
+## ##################################################
+
+include_HEADERS =              \
+       librpm.h                \
+       BinHeader.h             \
+       BinHeaderCache.h        \
+       RpmHeader.h             \
+       RpmHeaderCache.h        \
+       librpmDb.h              \
+       RpmDb.h
+
+
+noinst_LTLIBRARIES =   lib@PACKAGE@_target_rpm.la
+
+## ##################################################
+
+lib@PACKAGE@_target_rpm_la_SOURCES = \
+       BinHeader.cc            \
+       BinHeaderCache.cc       \
+       RpmHeader.cc            \
+       RpmHeaderCache.cc       \
+       librpmDb.cc             \
+       RpmDb.cc
+
+
+lib@PACKAGE@_target_rpm_la_LIBADD =
+
+## ##################################################
diff --git a/zypp/target/rpm/RpmDb.cc b/zypp/target/rpm/RpmDb.cc
new file mode 100644 (file)
index 0000000..f8ab8bc
--- /dev/null
@@ -0,0 +1,1987 @@
+/*---------------------------------------------------------------------\
+|                                                                      |
+|                      __   __    ____ _____ ____                      |
+|                      \ \ / /_ _/ ___|_   _|___ \                     |
+|                       \ V / _` \___ \ | |   __) |                    |
+|                        | | (_| |___) || |  / __/                     |
+|                        |_|\__,_|____/ |_| |_____|                    |
+|                                                                      |
+|                            Package Management                        |
+|                                                     (C) 2002 SuSE AG |
+\----------------------------------------------------------------------/
+
+   File:       RpmDb.cc
+   Purpose:    Interface to installed RPM system
+   Author:     Stefan Schubert <schubi@suse.de>
+   Maintainer: Ludwig Nussel <lnussel@suse.de>
+
+   Copied and adapted from agent-targetpkg
+
+/-*/
+#include "librpm.h"
+
+#include <cstdlib>
+#include <cstdio>
+#include <ctime>
+
+#include <iostream>
+#include <fstream>
+#include <list>
+#include <map>
+#include <string>
+#include <vector>
+
+#include <y2util/Date.h>
+#include <y2util/FSize.h>
+#include <y2util/Vendor.h>
+#include <y2util/Y2SLog.h>
+#include <y2util/TagParser.h>
+#include <y2util/Pathname.h>
+#include <y2util/PathInfo.h>
+#include <y2util/ExternalDataSource.h>
+#include <y2util/diff.h>
+
+#include <y2pm/RpmDb.h>
+#include <y2pm/InstTargetError.h>
+#include <y2pm/RpmDbCallbacks.h>
+
+#include <y2pm/librpmDb.h>
+#include <y2pm/PMRpmPackageDataProvider.h>
+#include <y2pm/PMPackageManager.h>
+#include <y2pm/Timecount.h>
+
+#include <Y2PM.h>
+
+#ifndef _
+#define _(X) X
+#endif
+
+using namespace std;
+using namespace RpmDbCallbacks;
+
+/******************************************************************
+**
+**
+**     FUNCTION NAME : stringPath
+**     FUNCTION TYPE : inline string
+*/
+inline string stringPath( const Pathname & root_r, const Pathname & sub_r )
+{
+  return librpmDb::stringPath( root_r, sub_r );
+}
+
+/******************************************************************
+**
+**
+**     FUNCTION NAME : operator<<
+**     FUNCTION TYPE : ostream &
+*/
+ostream & operator<<( ostream & str, const RpmDb::DbStateInfoBits & obj )
+{
+  if ( obj == RpmDb::DbSI_NO_INIT ) {
+    str << "NO_INIT";
+  } else {
+#define ENUM_OUT(B,C) str << ( obj & RpmDb::B ? C : '-' )
+    str << "V4(";
+    ENUM_OUT( DbSI_HAVE_V4,    'X' );
+    ENUM_OUT( DbSI_MADE_V4,    'c' );
+    ENUM_OUT( DbSI_MODIFIED_V4,        'm' );
+    str << ")V3(";
+    ENUM_OUT( DbSI_HAVE_V3,    'X' );
+    ENUM_OUT( DbSI_HAVE_V3TOV4,        'B' );
+    ENUM_OUT( DbSI_MADE_V3TOV4,        'c' );
+    str << ")";
+#undef ENUM_OUT
+  }
+  return str;
+}
+
+///////////////////////////////////////////////////////////////////
+//     CLASS NAME : RpmDbPtr
+//     CLASS NAME : RpmDbconstPtr
+///////////////////////////////////////////////////////////////////
+IMPL_BASE_POINTER(RpmDb);
+
+#define WARNINGMAILPATH "/var/log/YaST2/"
+#define FILEFORBACKUPFILES "YaSTBackupModifiedFiles"
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : RpmDb::Logfile
+/**
+ * Simple wrapper for progress log. Refcnt, filename and corresponding
+ * ofstream are static members. Logfile constructor raises, destructor
+ * lowers refcounter. On refcounter changing from 0->1, file is opened.
+ * Changing from 1->0 the file is closed. Thus Logfile objects should be
+ * local to those functions, writing the log, and must not be stored
+ * permanently;
+ *
+ * Usage:
+ *  some methothd ()
+ *  {
+ *    Logfile progresslog;
+ *    ...
+ *    progresslog() << "some message" << endl;
+ *    ...
+ *  }
+ **/
+class RpmDb::Logfile {
+  Logfile( const Logfile & );
+  Logfile & operator=( const Logfile & );
+  private:
+    static ofstream _log;
+    static unsigned _refcnt;
+    static Pathname _fname;
+    static void openLog() {
+      if ( !_fname.empty() ) {
+       _log.clear();
+       _log.open( _fname.asString().c_str(), std::ios::out|std::ios::app );
+       if( !_log )
+         ERR << "Could not open logfile '" << _fname << "'" << endl;
+      }
+    }
+    static void closeLog() {
+      _log.clear();
+      _log.close();
+    }
+    static void refUp() {
+      if ( !_refcnt )
+       openLog();
+      ++_refcnt;
+    }
+    static void refDown() {
+      --_refcnt;
+      if ( !_refcnt )
+       closeLog();
+    }
+  public:
+    Logfile() { refUp(); }
+    ~Logfile() { refDown(); }
+    ostream & operator()( bool timestamp = false ) {
+      if ( timestamp ) {
+       _log << Date::form( "%Y-%m-%d %H:%M:%S ", Date::now() );
+      }
+      return _log;
+    }
+    static void setFname( const Pathname & fname_r ) {
+      MIL << "installation log file " << fname_r << endl;
+      if ( _refcnt )
+       closeLog();
+      _fname = fname_r;
+      if ( _refcnt )
+       openLog();
+    }
+};
+
+///////////////////////////////////////////////////////////////////
+
+Pathname RpmDb::Logfile::_fname;
+ofstream RpmDb::Logfile::_log;
+unsigned RpmDb::Logfile::_refcnt = 0;
+
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::setInstallationLogfile
+//     METHOD TYPE : bool
+//
+bool RpmDb::setInstallationLogfile( const Pathname & filename )
+{
+  Logfile::setFname( filename );
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : RpmDb::Packages
+/**
+ * Helper class for RpmDb::getPackages() to build the
+ * list<PMPackagePtr> returned. We have to assert, that there
+ * is a unique entry for every PkgName.
+ *
+ * In the first step we build the _index map which helps to catch
+ * multiple occurances of a PkgName in the rpmdb. That's not desired,
+ * but possible. Usg. the last package instance installed is strored
+ * in the _index map.
+ *
+ * At the end buildList() is called to build the list<PMPackagePtr>
+ * from the _index map. _valid is set true to assign that the list
+ * is in sync with the rpmdb content. Operations changing the rpmdb
+ * content (install/remove package) should set _valid to false. The
+ * next call to RpmDb::getPackages() will then reread the the rpmdb.
+ *
+ * Note that outside RpmDb::getPackages() _list and _index are always
+ * in sync. So you may use lookup(PkgName) to retrieve a specific
+ * PMPackagePtr.
+ **/
+class RpmDb::Packages {
+  public:
+    list<PMPackagePtr>        _list;
+    map<PkgName,PMPackagePtr> _index;
+    bool                      _valid;
+    Packages() : _valid( false ) {}
+    void clear() {
+      _list.clear();
+      _index.clear();
+      _valid = false;
+    }
+    PMPackagePtr lookup( const PkgName & name_r ) const {
+      map<PkgName,PMPackagePtr>::const_iterator got = _index.find( name_r );
+      if ( got != _index.end() )
+       return got->second;
+      return PMPackagePtr();
+    }
+    void buildList() {
+      _list.clear();
+      for ( map<PkgName,PMPackagePtr>::iterator iter = _index.begin();
+           iter != _index.end(); ++iter ) {
+       if ( iter->second )
+         _list.push_back( iter->second );
+      }
+      _valid = true;
+    }
+};
+
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : RpmDb
+//
+///////////////////////////////////////////////////////////////////
+
+#define FAILIFNOTINITIALIZED if( ! initialized() ) { WAR << "No database access: " << Error::E_RpmDB_not_open << endl; return Error::E_RpmDB_not_open; }
+
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::RpmDb
+//     METHOD TYPE : Constructor
+//
+RpmDb::RpmDb()
+    : _dbStateInfo( DbSI_NO_INIT )
+    , _packages( * new Packages ) // delete in destructor
+#warning Check for obsolete memebers
+    , _backuppath ("/var/adm/backup")
+    , _packagebackups(false)
+    , _warndirexists(false)
+{
+   process = 0;
+   exit_code = -1;
+
+   // Some rpm versions are patched not to abort installation if
+   // symlink creation failed.
+   setenv( "RPM_IgnoreFailedSymlinks", "1", 1 );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::~RpmDb
+//     METHOD TYPE : Destructor
+//
+RpmDb::~RpmDb()
+{
+   M__ << "~RpmDb()" << endl;
+   closeDatabase();
+
+   delete process;
+   delete &_packages;
+   M__  << "~RpmDb() end" << endl;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::dumpOn
+//     METHOD TYPE : std::ostream &
+//
+std::ostream & RpmDb::dumpOn( std::ostream & str ) const
+{
+  str << "RpmDb[";
+
+  if ( _dbStateInfo == DbSI_NO_INIT ) {
+    str << "NO_INIT";
+  } else {
+#define ENUM_OUT(B,C) str << ( _dbStateInfo & B ? C : '-' )
+    str << "V4(";
+    ENUM_OUT( DbSI_HAVE_V4,    'X' );
+    ENUM_OUT( DbSI_MADE_V4,    'c' );
+    ENUM_OUT( DbSI_MODIFIED_V4,        'm' );
+    str << ")V3(";
+    ENUM_OUT( DbSI_HAVE_V3,    'X' );
+    ENUM_OUT( DbSI_HAVE_V3TOV4,        'B' );
+    ENUM_OUT( DbSI_MADE_V3TOV4,        'c' );
+    str << "): " << stringPath( _root, _dbPath );
+#undef ENUM_OUT
+  }
+  return str << "]";
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::initDatabase
+//     METHOD TYPE : PMError
+//
+PMError RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r )
+{
+  ///////////////////////////////////////////////////////////////////
+  // Check arguments
+  ///////////////////////////////////////////////////////////////////
+  if ( root_r.empty() )
+    root_r = "/";
+
+  if ( dbPath_r.empty() )
+    dbPath_r = "/var/lib/rpm";
+
+  if ( ! (root_r.absolute() && dbPath_r.absolute()) ) {
+    ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
+    return Error::E_invalid_argument;
+  }
+
+  MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r ) << endl;
+
+  ///////////////////////////////////////////////////////////////////
+  // Check whether already initialized
+  ///////////////////////////////////////////////////////////////////
+  if ( initialized() ) {
+    if ( root_r == _root && dbPath_r == _dbPath ) {
+      return Error::E_ok;
+    } else {
+      ERR << "Can't switch to " << stringPath( root_r, dbPath_r )
+       << " while accessing " << stringPath( _root, _dbPath ) << endl;
+      return Error::E_RpmDB_already_open;
+    }
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  // init database
+  ///////////////////////////////////////////////////////////////////
+  librpmDb::unblockAccess();
+  DbStateInfoBits info = DbSI_NO_INIT;
+  PMError err = internal_initDatabase( root_r, dbPath_r, info );
+
+  if ( err ) {
+    librpmDb::blockAccess();
+    ERR << "Cleanup on error: state " << info << endl;
+
+    if ( dbsi_has( info, DbSI_MADE_V4 ) ) {
+      // remove the newly created rpm4 database and
+      // any backup created on conversion.
+      removeV4( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
+    }
+
+  } else {
+    if ( dbsi_has( info, DbSI_HAVE_V3 ) ) {
+      if ( root_r == "/" || dbsi_has( info, DbSI_MODIFIED_V4 ) ) {
+       // Move obsolete rpm3 database beside.
+       MIL << "Cleanup: state " << info << endl;
+       removeV3( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
+       dbsi_clr( info, DbSI_HAVE_V3 );
+      } else {
+       // Performing an update: Keep the original rpm3 database
+       // and wait if the rpm4 database gets modified by installing
+       // or removing packages. Cleanup in modifyDatabase or closeDatabase.
+       MIL << "Update mode: Cleanup delayed until closeOldDatabase." << endl;
+      }
+    }
+#warning CHECK: notify root about conversion backup.
+
+    _root   = root_r;
+    _dbPath = dbPath_r;
+    _dbStateInfo = info;
+
+    if ( ! ( err || Y2PM::runningFromSystem() ) ) {
+      if (      dbsi_has( info, DbSI_HAVE_V4 )
+          && ! dbsi_has( info, DbSI_MADE_V4 ) ) {
+       err = rebuildDatabase();
+      }
+    }
+
+    // Close the database in case any write acces (create/convert)
+    // happened during init. This should drop any lock acquired
+    // by librpm. On demand it will be reopened readonly and should
+    // not hold any lock.
+    librpmDb::dbRelease( true );
+    MIL << "InitDatabase: " << *this << endl;
+  }
+
+  return err;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::internal_initDatabase
+//     METHOD TYPE : PMError
+//
+PMError RpmDb::internal_initDatabase( const Pathname & root_r, const Pathname & dbPath_r,
+                                     DbStateInfoBits & info_r )
+{
+  info_r = DbSI_NO_INIT;
+
+  ///////////////////////////////////////////////////////////////////
+  // Get info about the desired database dir
+  ///////////////////////////////////////////////////////////////////
+  librpmDb::DbDirInfo dbInfo( root_r, dbPath_r );
+
+  if ( dbInfo.illegalArgs() ) {
+    return Error::E_invalid_argument; // should not happen (checked in initDatabase)
+  }
+  if ( ! dbInfo.usableArgs() ) {
+    ERR << "Bad database directory: " << dbInfo.dbDir() << endl;
+    return Error::E_invalid_argument;
+  }
+
+  if ( dbInfo.hasDbV4() ) {
+    dbsi_set( info_r, DbSI_HAVE_V4 );
+    MIL << "Found rpm4 database in " << dbInfo.dbDir() << endl;
+  } else {
+    MIL << "Creating new rpm4 database in " << dbInfo.dbDir() << endl;
+  }
+
+  if ( dbInfo.hasDbV3() ) {
+    dbsi_set( info_r, DbSI_HAVE_V3 );
+  }
+  if ( dbInfo.hasDbV3ToV4() ) {
+    dbsi_set( info_r, DbSI_HAVE_V3TOV4 );
+  }
+
+  DBG << "Initial state: " << info_r << ": " << stringPath( root_r, dbPath_r );
+  librpmDb::dumpState( DBG ) << endl;
+
+  ///////////////////////////////////////////////////////////////////
+  // Access database, create if needed
+  ///////////////////////////////////////////////////////////////////
+
+  // creates dbdir and empty rpm4 database if not present
+  PMError err = librpmDb::dbAccess( root_r, dbPath_r );
+
+  if ( ! dbInfo.hasDbV4() ) {
+    dbInfo.restat();
+    if ( dbInfo.hasDbV4() ) {
+      dbsi_set( info_r, DbSI_HAVE_V4 | DbSI_MADE_V4 );
+    }
+  }
+
+  if ( err ) {
+    ERR << "Can't access rpm4 database " << dbInfo.dbV4() << " " << err << endl;
+    return err;
+  }
+
+  DBG << "Access state: " << info_r << ": " << stringPath( root_r, dbPath_r );
+  librpmDb::dumpState( DBG ) << endl;
+
+  ///////////////////////////////////////////////////////////////////
+  // Check whether to convert something. Create backup but do
+  // not remove anything here
+  ///////////////////////////////////////////////////////////////////
+  constlibrpmDbPtr dbptr;
+  librpmDb::dbAccess( dbptr );
+  bool dbEmpty = dbptr->empty();
+  if ( dbEmpty ) {
+    MIL << "Empty rpm4 database "  << dbInfo.dbV4() << endl;
+  }
+
+  if ( dbInfo.hasDbV3() ) {
+    MIL << "Found rpm3 database " << dbInfo.dbV3() << endl;
+
+    if ( dbEmpty ) {
+      extern PMError convertV3toV4( const Pathname & v3db_r, const constlibrpmDbPtr & v4db_r );
+
+      err = convertV3toV4( dbInfo.dbV3().path(), dbptr );
+      if ( err ) {
+       return err;
+      }
+
+      // create a backup copy
+      int res = PathInfo::copy( dbInfo.dbV3().path(), dbInfo.dbV3ToV4().path() );
+      if ( res ) {
+       WAR << "Backup converted rpm3 database failed: error(" << res << ")" << endl;
+      } else {
+       dbInfo.restat();
+       if ( dbInfo.hasDbV3ToV4() ) {
+         MIL << "Backup converted rpm3 database: " << dbInfo.dbV3ToV4() << endl;
+         dbsi_set( info_r, DbSI_HAVE_V3TOV4 | DbSI_MADE_V3TOV4 );
+       }
+      }
+
+    } else {
+
+      WAR << "Non empty rpm3 and rpm4 database found: using rpm4" << endl;
+#warning EXCEPTION: nonempty rpm4 and rpm3 database found.
+      //ConvertDbReport::Send report( RpmDbCallbacks::convertDbReport );
+      //report->start( dbInfo.dbV3().path() );
+      //report->stop( some error );
+
+      // set DbSI_MODIFIED_V4 as it's not a temporary which can be removed.
+      dbsi_set( info_r, DbSI_MODIFIED_V4 );
+
+    }
+
+    DBG << "Convert state: " << info_r << ": " << stringPath( root_r, dbPath_r );
+    librpmDb::dumpState( DBG ) << endl;
+  }
+
+  if ( dbInfo.hasDbV3ToV4() ) {
+    MIL << "Rpm3 database backup: " << dbInfo.dbV3ToV4() << endl;
+  }
+
+  return err;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::removeV4
+//     METHOD TYPE : void
+//
+void RpmDb::removeV4( const Pathname & dbdir_r, bool v3backup_r )
+{
+  const char * v3backup = "packages.rpm3";
+  const char * master = "Packages";
+  const char * index[] = {
+    "Basenames",
+    "Conflictname",
+    "Depends",
+    "Dirnames",
+    "Filemd5s",
+    "Group",
+    "Installtid",
+    "Name",
+    "Providename",
+    "Provideversion",
+    "Pubkeys",
+    "Requirename",
+    "Requireversion",
+    "Sha1header",
+    "Sigmd5",
+    "Triggername",
+    // last entry!
+    NULL
+  };
+
+  PathInfo pi( dbdir_r );
+  if ( ! pi.isDir() ) {
+    ERR << "Can't remove rpm4 database in non directory: " << dbdir_r << endl;
+    return;
+  }
+
+  for ( const char ** f = index; *f; ++f ) {
+    pi( dbdir_r + *f );
+    if ( pi.isFile() ) {
+      PathInfo::unlink( pi.path() );
+    }
+  }
+
+  pi( dbdir_r + master );
+  if ( pi.isFile() ) {
+    MIL << "Removing rpm4 database " << pi << endl;
+    PathInfo::unlink( pi.path() );
+  }
+
+  if ( v3backup_r ) {
+    pi( dbdir_r + v3backup );
+    if ( pi.isFile() ) {
+      MIL << "Removing converted rpm3 database backup " << pi << endl;
+      PathInfo::unlink( pi.path() );
+    }
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::removeV3
+//     METHOD TYPE : void
+//
+void RpmDb::removeV3( const Pathname & dbdir_r, bool v3backup_r )
+{
+  const char * master = "packages.rpm";
+  const char * index[] = {
+    "conflictsindex.rpm",
+    "fileindex.rpm",
+    "groupindex.rpm",
+    "nameindex.rpm",
+    "providesindex.rpm",
+    "requiredby.rpm",
+    "triggerindex.rpm",
+    // last entry!
+    NULL
+  };
+
+  PathInfo pi( dbdir_r );
+  if ( ! pi.isDir() ) {
+    ERR << "Can't remove rpm3 database in non directory: " << dbdir_r << endl;
+    return;
+  }
+
+  for ( const char ** f = index; *f; ++f ) {
+    pi( dbdir_r + *f );
+    if ( pi.isFile() ) {
+      PathInfo::unlink( pi.path() );
+    }
+  }
+
+#warning CHECK: compare vs existing v3 backup. notify root
+  pi( dbdir_r + master );
+  if ( pi.isFile() ) {
+    Pathname m( pi.path() );
+    if ( v3backup_r ) {
+      // backup was already created
+      PathInfo::unlink( m );
+      Pathname b( m.extend( "3" ) );
+      pi( b ); // stat backup
+    } else {
+      Pathname b( m.extend( ".deleted" ) );
+      pi( b );
+      if ( pi.isFile() ) {
+       // rempve existing backup
+       PathInfo::unlink( b );
+      }
+      PathInfo::rename( m, b );
+      pi( b ); // stat backup
+    }
+    MIL << "(Re)moved rpm3 database to " << pi << endl;
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::modifyDatabase
+//     METHOD TYPE : void
+//
+void RpmDb::modifyDatabase()
+{
+  if ( ! initialized() )
+    return;
+
+  // tag database as modified
+  dbsi_set( _dbStateInfo, DbSI_MODIFIED_V4 );
+
+  // Move outdated rpm3 database beside.
+  if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) ) {
+    MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
+    removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
+    dbsi_clr( _dbStateInfo, DbSI_HAVE_V3 );
+  }
+
+  // invalidate Packages list
+  _packages._valid = false;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::closeDatabase
+//     METHOD TYPE : PMError
+//
+PMError RpmDb::closeDatabase()
+{
+  if ( ! initialized() ) {
+    return Error::E_ok;
+  }
+
+  MIL << "Calling closeDatabase: " << *this << endl;
+
+  ///////////////////////////////////////////////////////////////////
+  // Block further database access
+  ///////////////////////////////////////////////////////////////////
+  _packages.clear();
+  librpmDb::blockAccess();
+
+  ///////////////////////////////////////////////////////////////////
+  // Check fate if old version database still present
+  ///////////////////////////////////////////////////////////////////
+  if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) ) {
+    MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
+    if ( dbsi_has( _dbStateInfo, DbSI_MODIFIED_V4 ) ) {
+      // Move outdated rpm3 database beside.
+      removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 )  );
+    } else {
+      // Remove unmodified rpm4 database
+      removeV4( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
+    }
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  // Uninit
+  ///////////////////////////////////////////////////////////////////
+  _root = _dbPath = Pathname();
+  _dbStateInfo = DbSI_NO_INIT;
+
+  MIL << "closeDatabase: " << *this << endl;
+  return Error::E_ok;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::rebuildDatabase
+//     METHOD TYPE : PMError
+//
+PMError RpmDb::rebuildDatabase()
+{
+  FAILIFNOTINITIALIZED;
+
+  MIL << "RpmDb::rebuildDatabase" << *this << endl;
+  Timecount _t( "RpmDb::rebuildDatabase" );
+
+  PathInfo dbMaster( root() + dbPath() + "Packages" );
+  PathInfo dbMasterBackup( dbMaster.path().extend( ".y2backup" ) );
+
+  // report
+  RebuildDbReport::Send report( rebuildDbReport );
+  report->start();
+  ProgressData pd;
+  // current size should be upper limit for new db
+  report->progress( pd.init( dbMaster.size() ) );
+
+  // run rpm
+  RpmArgVec opts;
+  opts.push_back("--rebuilddb");
+  opts.push_back("-vv");
+
+  // don't call modifyDatabase because it would remove the old
+  // rpm3 database, if the current database is a temporary one.
+  // But do invalidate packages list.
+  _packages._valid = false;
+  run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
+
+  // progress report: watch this file growing
+  PathInfo newMaster( root()
+                     + dbPath().extend( stringutil::form( "rebuilddb.%d",
+                                                          process?process->getpid():0) )
+                     + "Packages" );
+
+  string       line;
+  string       errmsg;
+
+  while ( systemReadLine( line ) ) {
+    if ( newMaster() ) { // file is removed at the end of rebuild.
+      report->progress( pd.set( newMaster.size() ) );
+    }
+
+    if ( line.compare( 0, 2, "D:" ) ) {
+      errmsg += line + '\n';
+      report->notify( line );
+      WAR << line << endl;
+    }
+  }
+
+  int rpm_status = systemStatus();
+
+  // evaluate result
+  PMError err;
+
+  if ( rpm_status != 0 ) {
+    ERR << "rpm failed, message was:" << endl << errmsg; // has trailing NL
+    err =  Error::E_RpmDB_subprocess_failed;
+    err.setDetails(errmsg);
+  } else {
+    report->progress( pd.toMax() ); // 100%
+  }
+
+  report->stop( err );
+  return err;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::importPubkey
+//     METHOD TYPE : PMError
+//
+PMError RpmDb::importPubkey( const Pathname & pubkey_r )
+{
+  FAILIFNOTINITIALIZED;
+  PMError err;
+
+  RpmArgVec opts;
+  opts.push_back ( "--import" );
+  opts.push_back ( "--" );
+  opts.push_back ( pubkey_r.asString().c_str() );
+
+  // don't call modifyDatabase because it would remove the old
+  // rpm3 database, if the current database is a temporary one.
+  // But do invalidate packages list.
+  _packages._valid = false;
+  run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
+
+  string line;
+  while ( systemReadLine( line ) ) {
+    if ( line.substr( 0, 6 ) == "error:" ) {
+      WAR << line << endl;
+    } else {
+      DBG << line << endl;
+    }
+  }
+
+  int rpm_status = systemStatus();
+
+  if ( rpm_status != 0 ) {
+    ERR << "Failed to import public key from file " << pubkey_r << ": rpm returned  " << rpm_status << endl;
+    err = Error::E_RpmDB_subprocess_failed;
+  } else {
+    MIL << "Imported public key from file " << pubkey_r << endl;
+  }
+
+  return err;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::importPubkey
+//     METHOD TYPE : PMError
+//
+PMError RpmDb::importPubkey( const Pathname & keyring_r, const string & keyname_r )
+{
+  FAILIFNOTINITIALIZED;
+
+  // create tempfile
+  char tmpname[] = "/tmp/y2.pubkey.XXXXXX";
+  int tmpfd = mkstemp( tmpname );
+  if ( tmpfd == -1 ) {
+    ERR << "Unable to create a unique temporary file for pubkey" << endl;
+    return Error::E_RpmDB_subprocess_failed;
+  }
+
+  // export keyname from keyring
+  RpmArgVec args;
+  args.push_back( "gpg" );
+  args.push_back( "--armor" );
+  args.push_back( "--no-default-keyring" );
+  args.push_back( "--keyring" );
+  args.push_back( keyring_r.asString().c_str() );
+  args.push_back( "--export" );
+  args.push_back( keyname_r.c_str() );
+
+  const char * argv[args.size() + 1];
+  const char ** p = argv;
+  p = copy( args.begin(), args.end(), p );
+  *p = 0;
+
+  // launch gpg
+  ExternalProgram prg( argv, ExternalProgram::Discard_Stderr, false, -1, true );
+  PMError err;
+  int res = 0;
+
+  // read key
+  for ( string line( prg.receiveLine() ); line.length(); line = prg.receiveLine() ) {
+    ssize_t written = write( tmpfd, line.c_str(), line.length() );
+    if ( written == -1 || unsigned(written) != line.length() ) {
+      ERR << "Error writing pubkey to " << tmpname << endl;
+      err = Error::E_RpmDB_subprocess_failed;
+      break;
+    }
+    res += written; // empty file indicates key not found
+  }
+  close( tmpfd );
+
+  if ( ! res ) {
+    WAR << "gpg: no key '" << keyname_r << "' found in  '" << keyring_r << "'" << endl;
+    err = Error::E_RpmDB_subprocess_failed;
+  }
+
+  // check gpg returncode
+  res = prg.close();
+  if ( res ) {
+    ERR << "gpg: export '" << keyname_r << "' from '" << keyring_r << "' returned " << res << endl;
+    err = Error::E_RpmDB_subprocess_failed;
+  }
+
+  if ( ! err ) {
+    MIL << "Exported '" << keyname_r << "' from '" << keyring_r << "' to " << tmpname << endl;
+    err = importPubkey( tmpname );
+  }
+
+  // remove tempfile
+  PathInfo::unlink( tmpname );
+
+  return err;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::pubkeys
+//     METHOD TYPE : set<PkgEdition>
+//
+set<PkgEdition> RpmDb::pubkeys() const
+{
+  set<PkgEdition> ret;
+
+  librpmDb::db_const_iterator it;
+  for ( it.findByName( PkgName( "gpg-pubkey" ) ); *it; ++it ) {
+    ret.insert( it->tag_edition() );
+  }
+
+  return ret;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::packagesValid
+//     METHOD TYPE : bool
+//
+bool RpmDb::packagesValid() const
+{
+  return( _packages._valid || ! initialized() );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::getPackages
+//     METHOD TYPE : const std::list<PMPackagePtr> &
+//
+//     DESCRIPTION :
+//
+const std::list<PMPackagePtr> & RpmDb::getPackages()
+{
+  if ( packagesValid() ) {
+    return _packages._list;
+  }
+
+  // report
+  Timecount _t( "RpmDb::getPackages" );
+  ScanDbReport::Send report( scanDbReport );
+  report->start();
+
+#warning how to detect corrupt db while reading.
+
+  _packages.clear();
+
+  ///////////////////////////////////////////////////////////////////
+  // Collect package data. A map is used to check whethere there are
+  // multiple entries for the same PkgName. If so we consider the last
+  // one installed to be the one we're interesed in.
+  ///////////////////////////////////////////////////////////////////
+  ProgressData pd;
+  librpmDb::db_const_iterator iter; // findAll
+  {
+    // quick check
+    unsigned expect = 0;
+    for ( ; *iter; ++iter ) {
+      ++expect;
+    }
+    if ( iter.dbError() ) {
+      ERR << "No database access: " << iter.dbError() << endl;
+      report->stop( iter.dbError() );
+      return _packages._list;
+    }
+    report->progress( pd.init( expect ) );
+  }
+
+  for ( iter.findAll(); *iter; ++iter, report->progress( pd.incr() ) ) {
+
+    PkgName name = iter->tag_name();
+    if ( name == PkgName( "gpg-pubkey" ) ) {
+      // pseudo package filtered, as we can't handle multiple instances
+      // of 'gpg-pubkey-VERS-REL'.
+      continue;
+    }
+    Date installtime = iter->tag_installtime();
+    PMPackagePtr & nptr = _packages._index[name]; // be shure to get a reference!
+
+    if ( nptr ) {
+      WAR << "Multiple entries for package '" << name << "' in rpmdb" << endl;
+      if ( nptr->installtime() > installtime )
+       continue;
+      // else overwrite previous entry
+    }
+
+    // create dataprovider and package
+    PMRpmPackageDataProviderPtr ndp = new PMRpmPackageDataProvider( this );
+    nptr = new PMPackage( name, iter->tag_edition(), iter->tag_arch(), ndp );
+
+    // add PMSolvable data to package, collect filerequires on the fly
+    nptr->setProvides ( iter->tag_provides ( &_filerequires ) );
+    nptr->setRequires ( iter->tag_requires ( &_filerequires ) );
+    nptr->setConflicts( iter->tag_conflicts( &_filerequires ) );
+    nptr->setObsoletes( iter->tag_obsoletes( &_filerequires ) );
+
+    // let dataprovider collect static data
+    ndp->loadStaticData( *iter );
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  // Evaluate filerequires collected so far
+  ///////////////////////////////////////////////////////////////////
+  for( FileDeps::FileNames::iterator it = _filerequires.begin(); it != _filerequires.end(); ++it ) {
+
+    for ( iter.findByFile( *it ); *iter; ++iter ) {
+      PMPackagePtr pptr = _packages.lookup( iter->tag_name() );
+      if ( !pptr ) {
+       WAR << "rpmdb.findByFile returned unpknown package " << *iter << endl;
+       continue;
+      }
+      pptr->addProvides( *it );
+    }
+
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  // Build final packages list
+  ///////////////////////////////////////////////////////////////////
+  _packages.buildList();
+  DBG << "Found installed packages: " << _packages._list.size() << endl;
+  report->stop( PMError::E_ok );
+  return _packages._list;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::traceFileRel
+//     METHOD TYPE : void
+//
+//     DESCRIPTION :
+//
+void RpmDb::traceFileRel( const PkgRelation & rel_r )
+{
+  if ( ! rel_r.isFileRel() )
+    return;
+
+  if ( ! _filerequires.insert( rel_r.name() ).second )
+    return; // already got it in _filerequires
+
+  if ( ! _packages._valid )
+    return; // collect only. Evaluated in first call to getPackages()
+
+  //
+  // packages already initialized. Must check and insert here
+  //
+  librpmDb::db_const_iterator iter;
+  if ( iter.dbError() ) {
+    ERR << "No database access: " << iter.dbError() << endl;
+    return;
+  }
+
+  for ( iter.findByFile( rel_r.name() ); *iter; ++iter ) {
+    PMPackagePtr pptr = _packages.lookup( iter->tag_name() );
+    if ( !pptr ) {
+      WAR << "rpmdb.findByFile returned unpknown package " << *iter << endl;
+      continue;
+    }
+    pptr->addProvides( rel_r.name() );
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::hasFile
+//     METHOD TYPE : bool
+//
+//     DESCRIPTION :
+//
+bool RpmDb::hasFile( const std::string & file_r ) const
+{
+  librpmDb::db_const_iterator it;
+  return it.findByFile( file_r );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::hasProvides
+//     METHOD TYPE : bool
+//
+//     DESCRIPTION :
+//
+bool RpmDb::hasProvides( const std::string & tag_r ) const
+{
+  librpmDb::db_const_iterator it;
+  return it.findByProvides( tag_r );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::hasRequiredBy
+//     METHOD TYPE : bool
+//
+//     DESCRIPTION :
+//
+bool RpmDb::hasRequiredBy( const std::string & tag_r ) const
+{
+  librpmDb::db_const_iterator it;
+  return it.findByRequiredBy( tag_r );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::hasConflicts
+//     METHOD TYPE : bool
+//
+//     DESCRIPTION :
+//
+bool RpmDb::hasConflicts( const std::string & tag_r ) const
+{
+  librpmDb::db_const_iterator it;
+  return it.findByConflicts( tag_r );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::hasPackage
+//     METHOD TYPE : bool
+//
+//     DESCRIPTION :
+//
+bool RpmDb::hasPackage( const PkgName & name_r ) const
+{
+  librpmDb::db_const_iterator it;
+  return it.findPackage( name_r );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::getData
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION :
+//
+PMError RpmDb::getData( const PkgName & name_r,
+                       constRpmHeaderPtr & result_r ) const
+{
+  librpmDb::db_const_iterator it;
+  it.findPackage( name_r );
+  result_r = *it;
+  return it.dbError();
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::getData
+//     METHOD TYPE : PMError
+//
+//     DESCRIPTION :
+//
+PMError RpmDb::getData( const PkgName & name_r, const PkgEdition & ed_r,
+                       constRpmHeaderPtr & result_r ) const
+{
+  librpmDb::db_const_iterator it;
+  it.findPackage( name_r, ed_r  );
+  result_r = *it;
+  return it.dbError();
+}
+
+/*--------------------------------------------------------------*/
+/* Checking the source rpm <rpmpath> with rpm --chcksig and     */
+/* the version number.                                         */
+/*--------------------------------------------------------------*/
+unsigned
+RpmDb::checkPackage (const Pathname & packagePath, string version, string md5 )
+{
+    unsigned result = 0;
+
+    if ( ! version.empty() ) {
+      constRpmHeaderPtr h( RpmHeader::readPackage( packagePath, RpmHeader::NOSIGNATURE ) );
+      if ( ! h || PkgEdition( version ) != h->tag_edition() ) {
+       result |= CHK_INCORRECT_VERSION;
+      }
+    }
+
+    if(!md5.empty())
+    {
+#warning TBD MD5 check
+       WAR << "md5sum check not yet implemented" << endl;
+       return CHK_INCORRECT_FILEMD5;
+    }
+
+    std::string path = packagePath.asString();
+    // checking --checksig
+    const char *const argv[] = {
+       "rpm", "--checksig", "--", path.c_str(), 0
+    };
+
+    exit_code = -1;
+
+    string output = "";
+    unsigned int k;
+    for ( k = 0; k < (sizeof(argv) / sizeof(*argv)) -1; k++ )
+    {
+       output = output + " " + argv[k];
+    }
+
+    DBG << "rpm command: " << output << endl;
+
+    if ( process != NULL )
+    {
+       delete process;
+       process = NULL;
+    }
+    // Launch the program
+    process = new ExternalProgram( argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
+
+
+    if ( process == NULL )
+    {
+       result |= CHK_OTHER_FAILURE;
+       D__ << "create process failed" << endl;
+    }
+
+    string value;
+    output = process->receiveLine();
+
+    while ( output.length() > 0)
+    {
+       string::size_type         ret;
+
+       // extract \n
+       ret = output.find_first_of ( "\n" );
+       if ( ret != string::npos )
+       {
+           value.assign ( output, 0, ret );
+       }
+       else
+       {
+           value = output;
+       }
+
+       D__ << "stdout: " << value << endl;
+
+       string::size_type pos;
+       if((pos = value.find (path)) != string::npos)
+       {
+           string rest = value.substr (pos + path.length() + 1);
+           if (rest.find("NOT OK") == string::npos)
+           {
+               // see what checks are ok
+               if (rest.find("md5") == string::npos)
+               {
+                   result |= CHK_MD5SUM_MISSING;
+               }
+               if (rest.find("gpg") == string::npos)
+               {
+                   result |= CHK_GPGSIG_MISSING;
+               }
+           }
+           else
+           {
+               // see what checks are not ok
+               if (rest.find("MD5") != string::npos)
+               {
+                   result |= CHK_INCORRECT_PKGMD5;
+               }
+               else
+               {
+                   result |= CHK_MD5SUM_MISSING;
+               }
+
+               if (rest.find("GPG") != string::npos)
+               {
+                   result |= CHK_INCORRECT_GPGSIG;
+               }
+               else
+               {
+                   result |= CHK_GPGSIG_MISSING;
+               }
+           }
+       }
+
+       output = process->receiveLine();
+    }
+
+    if ( result == 0 && systemStatus() != 0 )
+    {
+       // error
+       result |= CHK_OTHER_FAILURE;
+    }
+
+    return ( result );
+}
+
+// determine changed files of installed package
+bool
+RpmDb::queryChangedFiles(FileList & fileList, const string& packageName)
+{
+    bool ok = true;
+
+    fileList.clear();
+
+    if( ! initialized() ) return false;
+
+    RpmArgVec opts;
+
+    opts.push_back ("-V");
+    opts.push_back ("--nodeps");
+    opts.push_back ("--noscripts");
+    opts.push_back ("--nomd5");
+    opts.push_back ("--");
+    opts.push_back (packageName.c_str());
+
+    run_rpm (opts, ExternalProgram::Discard_Stderr);
+
+    if ( process == NULL )
+       return false;
+
+    /* from rpm manpage
+       5      MD5 sum
+       S      File size
+       L      Symlink
+       T      Mtime
+       D      Device
+       U      User
+       G      Group
+       M      Mode (includes permissions and file type)
+    */
+
+    string line;
+    while (systemReadLine(line))
+    {
+       if (line.length() > 12 &&
+           (line[0] == 'S' || line[0] == 's' ||
+            (line[0] == '.' && line[7] == 'T')))
+       {
+           // file has been changed
+           string filename;
+
+           filename.assign(line, 11, line.length() - 11);
+           fileList.insert(filename);
+       }
+    }
+
+    systemStatus();
+    // exit code ignored, rpm returns 1 no matter if package is installed or
+    // not
+
+    return ok;
+}
+
+
+
+/****************************************************************/
+/* private member-functions                                    */
+/****************************************************************/
+
+/*--------------------------------------------------------------*/
+/* Run rpm with the specified arguments, handling stderr       */
+/* as specified  by disp                                       */
+/*--------------------------------------------------------------*/
+void
+RpmDb::run_rpm (const RpmArgVec& opts,
+               ExternalProgram::Stderr_Disposition disp)
+{
+    if ( process ) {
+       delete process;
+       process = NULL;
+    }
+    exit_code = -1;
+
+    if ( ! initialized() ) {
+       ERR << "Attempt to run rpm: " << Error::E_RpmDB_not_open << endl;
+       return;
+    }
+
+    RpmArgVec args;
+
+    // always set root and dbpath
+    args.push_back("rpm");
+    args.push_back("--root");
+    args.push_back(_root.asString().c_str());
+    args.push_back("--dbpath");
+    args.push_back(_dbPath.asString().c_str());
+
+    const char* argv[args.size() + opts.size() + 1];
+
+    const char** p = argv;
+    p = copy (args.begin (), args.end (), p);
+    p = copy (opts.begin (), opts.end (), p);
+    *p = 0;
+
+    // Invalidate all outstanding database handles in case
+    // the database gets modified.
+    librpmDb::dbRelease( true );
+
+    // Launch the program with default locale
+    process = new ExternalProgram(argv, disp, false, -1, true);
+    return;
+}
+
+/*--------------------------------------------------------------*/
+/* Read a line from the rpm process                            */
+/*--------------------------------------------------------------*/
+bool
+RpmDb::systemReadLine(string &line)
+{
+    line.erase();
+
+    if ( process == NULL )
+       return false;
+
+    line = process->receiveLine();
+
+    if (line.length() == 0)
+       return false;
+
+    if (line[line.length() - 1] == '\n')
+       line.erase(line.length() - 1);
+
+    return true;
+}
+
+/*--------------------------------------------------------------*/
+/* Return the exit status of the rpm process, closing the      */
+/* connection if not already done                              */
+/*--------------------------------------------------------------*/
+int
+RpmDb::systemStatus()
+{
+   if ( process == NULL )
+      return -1;
+
+   exit_code = process->close();
+   process->kill();
+   delete process;
+   process = 0;
+
+//   D__ << "exit code " << exit_code << endl;
+
+  return exit_code;
+}
+
+/*--------------------------------------------------------------*/
+/* Forcably kill the rpm process                               */
+/*--------------------------------------------------------------*/
+void
+RpmDb::systemKill()
+{
+  if (process) process->kill();
+}
+
+
+// generate diff mails for config files
+void RpmDb::processConfigFiles(const string& line, const string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
+{
+    string msg = line.substr(9);
+    string::size_type pos1 = string::npos;
+    string::size_type pos2 = string::npos;
+    string file1s, file2s;
+    Pathname file1;
+    Pathname file2;
+
+    pos1 = msg.find (typemsg);
+    for (;;)
+    {
+       if( pos1 == string::npos )
+           break;
+
+       pos2 = pos1 + strlen (typemsg);
+
+       if (pos2 >= msg.length() )
+           break;
+
+       file1 = msg.substr (0, pos1);
+       file2 = msg.substr (pos2);
+
+       file1s = file1.asString();
+       file2s = file2.asString();
+
+       if (!_root.empty() && _root != "/")
+       {
+           file1 = _root + file1;
+           file2 = _root + file2;
+       }
+
+       string out;
+       int ret = Diff::differ (file1.asString(), file2.asString(), out, 25);
+       if (ret)
+       {
+           Pathname file = _root + WARNINGMAILPATH;
+           if (PathInfo::assert_dir(file) != 0)
+           {
+               ERR << "Could not create " << file.asString() << endl;
+               break;
+           }
+           file += Date::form("config_diff_%Y_%m_%d.log", Date::now());
+           ofstream notify(file.asString().c_str(), std::ios::out|std::ios::app);
+           if(!notify)
+           {
+               ERR << "Could not open " <<  file << endl;
+               break;
+           }
+
+           // Translator: %s = name of an rpm package. A list of diffs follows
+           // this message.
+           notify << stringutil::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
+           if(ret>1)
+           {
+               ERR << "diff failed" << endl;
+               notify << stringutil::form(difffailmsg,
+                   file1s.c_str(), file2s.c_str()) << endl;
+           }
+           else
+           {
+               notify << stringutil::form(diffgenmsg,
+                   file1s.c_str(), file2s.c_str()) << endl;
+
+               // remove root for the viewer's pleasure (#38240)
+               if (!_root.empty() && _root != "/")
+               {
+                   if(out.substr(0,4) == "--- ")
+                   {
+                       out.replace(4, file1.asString().length(), file1s);
+                   }
+                   string::size_type pos = out.find("\n+++ ");
+                   if(pos != string::npos)
+                   {
+                       out.replace(pos+5, file2.asString().length(), file2s);
+                   }
+               }
+               notify << out << endl;
+           }
+           notify.close();
+           notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
+           notify.close();
+       }
+       else
+       {
+           WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
+       }
+       break;
+    }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::installPackage
+//     METHOD TYPE : PMError
+//
+PMError RpmDb::installPackage( const Pathname & filename, unsigned flags )
+{
+    FAILIFNOTINITIALIZED;
+    Logfile progresslog;
+
+    MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
+
+    // report
+    InstallPkgReport::Send report( installPkgReport );
+    report->start( filename );
+    ProgressData pd;
+
+    // backup
+    if ( _packagebackups ) {
+      report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
+      if ( ! backupPackage( filename ) ) {
+       ERR << "backup of " << filename.asString() << " failed" << endl;
+      }
+      report->progress( pd.set( 0 ) ); // allow 1% for backup creation.
+    } else {
+      report->progress( pd.init( 100 ) );
+    }
+
+    // run rpm
+    RpmArgVec opts;
+    if (flags & RPMINST_NOUPGRADE)
+      opts.push_back("-i");
+    else
+      opts.push_back("-U");
+    opts.push_back("--percent");
+
+    if (flags & RPMINST_NODIGEST)
+       opts.push_back("--nodigest");
+    if (flags & RPMINST_NOSIGNATURE)
+       opts.push_back("--nosignature");
+    if (flags & RPMINST_NODOCS)
+       opts.push_back ("--excludedocs");
+    if (flags & RPMINST_NOSCRIPTS)
+       opts.push_back ("--noscripts");
+    if (flags & RPMINST_FORCE)
+       opts.push_back ("--force");
+    if (flags & RPMINST_NODEPS)
+       opts.push_back ("--nodeps");
+    if(flags & RPMINST_IGNORESIZE)
+       opts.push_back ("--ignoresize");
+    if(flags & RPMINST_JUSTDB)
+       opts.push_back ("--justdb");
+
+    opts.push_back("--");
+    opts.push_back (filename.asString().c_str());
+
+    modifyDatabase(); // BEFORE run_rpm
+    run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
+
+    string line;
+    string rpmmsg;
+    vector<string> configwarnings;
+    vector<string> errorlines;
+
+    while (systemReadLine(line))
+    {
+       if (line.substr(0,2)=="%%")
+       {
+           int percent;
+           sscanf (line.c_str () + 2, "%d", &percent);
+           report->progress( pd.set( percent ) );
+       }
+       else
+           rpmmsg += line+'\n';
+
+       if( line.substr(0,8) == "warning:" )
+       {
+           configwarnings.push_back(line);
+       }
+    }
+    int rpm_status = systemStatus();
+
+    // evaluate result
+    for(vector<string>::iterator it = configwarnings.begin();
+       it != configwarnings.end(); ++it)
+    {
+           processConfigFiles(*it, Pathname::basename(filename), " saved as ",
+               // %s = filenames
+               _("rpm saved %s as %s but it was impossible to determine the difference"),
+               // %s = filenames
+               _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
+           processConfigFiles(*it, Pathname::basename(filename), " created as ",
+               // %s = filenames
+               _("rpm created %s as %s but it was impossible to determine the difference"),
+               // %s = filenames
+               _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
+    }
+
+    PMError err;
+
+    if ( rpm_status != 0 )  {
+      // %s = filename of rpm package
+      progresslog(/*timestamp*/true) << stringutil::form(_("%s install failed"), Pathname::basename(filename).c_str()) << endl;
+      progresslog() << _("rpm output:") << endl << rpmmsg << endl;
+      ERR << "rpm failed, message was: " << rpmmsg << endl;
+      err = Error::E_RpmDB_subprocess_failed;
+      err.setDetails( rpmmsg );
+    } else {
+      // %s = filename of rpm package
+      progresslog(/*timestamp*/true) << stringutil::form(_("%s installed ok"), Pathname::basename(filename).c_str()) << endl;
+      if( ! rpmmsg.empty() ) {
+       progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
+      }
+    }
+
+    report->stop( err );
+    return err;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::removePackage
+//     METHOD TYPE : PMError
+//
+PMError RpmDb::removePackage( constPMPackagePtr package, unsigned flags )
+{
+  return removePackage( package->name(), flags );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::removePackage
+//     METHOD TYPE : PMError
+//
+PMError RpmDb::removePackage( const string & name_r, unsigned flags )
+{
+    FAILIFNOTINITIALIZED;
+    Logfile progresslog;
+
+    MIL << "RpmDb::removePackage(" << name_r << "," << flags << ")" << endl;
+
+    // report
+    RemovePkgReport::Send report( removePkgReport );
+    report->start( name_r );
+    ProgressData pd;
+
+    // backup
+    if ( _packagebackups ) {
+      report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
+      if ( ! backupPackage( name_r ) ) {
+       ERR << "backup of " << name_r << " failed" << endl;
+      }
+      report->progress( pd.set( 0 ) );
+    } else {
+      report->progress( pd.init( 100 ) );
+    }
+
+    // run rpm
+    RpmArgVec opts;
+    opts.push_back("-e");
+    opts.push_back("--allmatches");
+
+    if (flags & RPMINST_NOSCRIPTS)
+       opts.push_back("--noscripts");
+    if (flags & RPMINST_NODEPS)
+       opts.push_back("--nodeps");
+    if (flags & RPMINST_JUSTDB)
+       opts.push_back("--justdb");
+    if (flags & RPMINST_FORCE) {
+      WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
+    }
+
+    opts.push_back("--");
+    opts.push_back(name_r.c_str());
+
+    modifyDatabase(); // BEFORE run_rpm
+    run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
+
+    string line;
+    string rpmmsg;
+
+    // got no progress from command, so we fake it:
+    // 5  - command started
+    // 50 - command completed
+    // 100 if no error
+    report->progress( pd.set( 5 ) );
+    while (systemReadLine(line))
+    {
+       rpmmsg += line+'\n';
+    }
+    report->progress( pd.set( 50 ) );
+    int rpm_status = systemStatus();
+
+    // evaluate result
+    PMError err;
+
+    if ( rpm_status != 0 ) {
+      // %s = name of rpm package
+      progresslog(/*timestamp*/true) << stringutil::form(_("%s remove failed"), name_r.c_str()) << endl;
+      progresslog() << _("rpm output:") << endl << rpmmsg << endl;
+      ERR << "rpm failed, message was: " << rpmmsg << endl;
+      err =  Error::E_RpmDB_subprocess_failed;
+      err.setDetails( rpmmsg );
+    } else {
+      progresslog(/*timestamp*/true) << stringutil::form(_("%s remove ok"), name_r.c_str()) << endl;
+      if( ! rpmmsg.empty() ) {
+       progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
+      }
+    }
+
+    report->stop( err );
+    return err;
+}
+
+string
+RpmDb::checkPackageResult2string(unsigned code)
+{
+    string msg;
+    // begin of line characters
+    string bol = " - ";
+    // end of line characters
+    string eol = "\n";
+    if(code == 0)
+       return string(_("Ok"))+eol;
+
+    //translator: these are different kinds of how an rpm package can be broken
+    msg = _("Package is not OK for the following reasons:");
+    msg += eol;
+
+    if(code&CHK_INCORRECT_VERSION)
+    {
+       msg += bol;
+       msg+=_("Package contains different version than expected");
+       msg += eol;
+    }
+    if(code&CHK_INCORRECT_FILEMD5)
+    {
+       msg += bol;
+       msg+=_("Package file has incorrect MD5 sum");
+       msg += eol;
+    }
+    if(code&CHK_GPGSIG_MISSING)
+    {
+       msg += bol;
+       msg+=_("Package is not signed");
+       msg += eol;
+    }
+    if(code&CHK_MD5SUM_MISSING)
+    {
+       msg += bol;
+       msg+=_("Package has no MD5 sum");
+       msg += eol;
+    }
+    if(code&CHK_INCORRECT_GPGSIG)
+    {
+       msg += bol;
+       msg+=_("Package has incorrect signature");
+       msg += eol;
+    }
+    if(code&CHK_INCORRECT_PKGMD5)
+    {
+       msg += bol;
+       msg+=_("Package archive has incorrect MD5 sum");
+       msg += eol;
+    }
+    if(code&CHK_OTHER_FAILURE)
+    {
+       msg += bol;
+       msg+=_("rpm failed for unkown reason, see log file");
+       msg += eol;
+    }
+
+    return msg;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::backupPackage
+//     METHOD TYPE : bool
+//
+bool RpmDb::backupPackage( const Pathname & filename )
+{
+  constRpmHeaderPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
+  if( ! h )
+    return false;
+
+  return backupPackage( h->tag_name() );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmDb::backupPackage
+//     METHOD TYPE : bool
+//
+bool RpmDb::backupPackage(const string& packageName)
+{
+    Logfile progresslog;
+    bool ret = true;
+    Pathname backupFilename;
+    Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
+
+    if (_backuppath.empty())
+    {
+       INT << "_backuppath empty" << endl;
+       return false;
+    }
+
+    FileList fileList;
+
+    if (!queryChangedFiles(fileList, packageName))
+    {
+       ERR << "Error while getting changed files for package " <<
+           packageName << endl;
+       return false;
+    }
+
+    if (fileList.size() <= 0)
+    {
+       DBG <<  "package " <<  packageName << " not changed -> no backup" << endl;
+       return true;
+    }
+
+    if (PathInfo::assert_dir(_root + _backuppath) != 0)
+    {
+       return false;
+    }
+
+    {
+       // build up archive name
+       time_t currentTime = time(0);
+       struct tm *currentLocalTime = localtime(&currentTime);
+
+       int date = (currentLocalTime->tm_year + 1900) * 10000
+           + (currentLocalTime->tm_mon + 1) * 100
+           + currentLocalTime->tm_mday;
+
+       int num = 0;
+       do
+       {
+           backupFilename = _root + _backuppath
+               + stringutil::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
+
+       }
+       while ( PathInfo(backupFilename).isExist() && num++ < 1000);
+
+       PathInfo pi(filestobackupfile);
+       if(pi.isExist() && !pi.isFile())
+       {
+           ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
+           return false;
+       }
+
+       std::ofstream fp ( filestobackupfile.asString().c_str(), std::ios::out|std::ios::trunc );
+
+       if(!fp)
+       {
+           ERR << "could not open " << filestobackupfile.asString() << endl;
+           return false;
+       }
+
+       for (FileList::const_iterator cit = fileList.begin();
+           cit != fileList.end(); ++cit)
+       {
+           string name = *cit;
+           if ( name[0] == '/' )
+           {
+               // remove slash, file must be relative to -C parameter of tar
+               name = name.substr( 1 );
+           }
+           D__ << "saving file "<< name << endl;
+           fp << name << endl;
+       }
+       fp.close();
+
+       const char* const argv[] =
+       {
+           "tar",
+           "-czhP",
+           "-C",
+           _root.asString().c_str(),
+           "--ignore-failed-read",
+           "-f",
+           backupFilename.asString().c_str(),
+           "-T",
+           filestobackupfile.asString().c_str(),
+           NULL
+       };
+
+       // execute tar in inst-sys (we dont know if there is a tar below _root !)
+       ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
+
+       string tarmsg;
+
+       // TODO: its probably possible to start tar with -v and watch it adding
+       // files to report progress
+       for (string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
+       {
+           tarmsg+=output;
+       }
+
+       int ret = tar.close();
+
+       if ( ret != 0)
+       {
+           ERR << "tar failed: " << tarmsg << endl;
+           ret = false;
+       }
+       else
+       {
+           MIL << "tar backup ok" << endl;
+           progresslog(/*timestamp*/true) << stringutil::form(_("created backup %s"), backupFilename.asString().c_str()) << endl;
+       }
+
+       PathInfo::unlink(filestobackupfile);
+    }
+
+    return ret;
+}
+
+void RpmDb::setBackupPath(const Pathname& path)
+{
+    _backuppath = path;
+}
diff --git a/zypp/target/rpm/RpmDb.h b/zypp/target/rpm/RpmDb.h
new file mode 100644 (file)
index 0000000..c71c0e4
--- /dev/null
@@ -0,0 +1,508 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/RpmDb.h
+ *
+*/
+
+// -*- C++ -*-
+
+#ifndef RpmDb_h
+#define RpmDb_h
+
+#include <iosfwd>
+#include <list>
+#include <vector>
+#include <string>
+
+#include "zypp/Pathname.h"
+#include "zypp/ExternalProgram.h"
+
+#include "zypp/PMPackagePtr.h"
+
+namespace zypp {
+  namespace target {
+    namespace rpm {
+
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       CLASS NAME : RpmDb
+      /**
+       * @short Interface to the rpm program
+       **/
+      class RpmDb : public base::ReferenceCounted, private base::NonCopyable
+      {
+        REP_BODY(RpmDb);
+      
+        public:
+      
+          /**
+           * Default error class
+           **/
+          typedef class InstTargetError Error;
+      
+          ///////////////////////////////////////////////////////////////////
+          //
+          // INITALISATION
+          //
+          ///////////////////////////////////////////////////////////////////
+        private:
+      
+          enum DbStateInfoBits {
+            DbSI_NO_INIT       = 0x0000,
+            DbSI_HAVE_V4       = 0x0001,
+            DbSI_MADE_V4       = 0x0002,
+            DbSI_MODIFIED_V4   = 0x0004,
+            DbSI_HAVE_V3       = 0x0008,
+            DbSI_HAVE_V3TOV4   = 0x0010,
+            DbSI_MADE_V3TOV4   = 0x0020
+          };
+      
+          friend std::ostream & operator<<( std::ostream & str, const DbStateInfoBits & obj );
+      
+          void dbsi_set( DbStateInfoBits & val_r, const unsigned & bits_r ) const {
+            val_r = (DbStateInfoBits)(val_r | bits_r);
+          }
+          void dbsi_clr( DbStateInfoBits & val_r, const unsigned & bits_r ) const {
+            val_r = (DbStateInfoBits)(val_r & ~bits_r);
+          }
+          bool dbsi_has( const DbStateInfoBits & val_r, const unsigned & bits_r ) const {
+            return( (val_r & bits_r) == bits_r );
+          }
+      
+          /**
+           * Internal state info
+           **/
+          DbStateInfoBits _dbStateInfo;
+      
+          /**
+           * Root directory for all operations.
+           **/
+          Pathname _root;
+      
+          /**
+           * Directory that contains the rpmdb.
+           **/
+          Pathname _dbPath;
+      
+          /**
+           * Internal helper for @ref initDatabase.
+           **/
+          PMError internal_initDatabase( const Pathname & root_r, const Pathname & dbPath_r,
+                                  DbStateInfoBits & info_r );
+      
+          /**
+           * Remove the rpm4 database in dbdir_r and optionally any backup created
+           * on conversion.
+           **/
+          static void removeV4( const Pathname & dbdir_r, bool v3backup_r );
+      
+          /**
+           * Remove the rpm3 database in dbdir_r. Create a backup copy named
+           * packages.rpm3 if it does not already exist.
+           **/
+          static void removeV3( const Pathname & dbdir_r, bool v3backup_r );
+      
+          /**
+           * Called before the database is modified by installPackage/removePackage.
+           * Invalidates Packages list and moves away any old database.
+           **/
+          void modifyDatabase();
+      
+        public:
+      
+          /**
+           * Constructor. There's no rpmdb access until @ref initDatabase
+           * was called.
+           **/
+          RpmDb();
+      
+          /**
+           * Destructor.
+           **/
+          ~RpmDb();
+      
+          /**
+           * @return Root directory for all operations (empty if not initialized).
+           **/
+          const Pathname & root() const { return _root; }
+      
+          /**
+           * @return Directory that contains the rpmdb (empty if not initialized).
+           **/
+          const Pathname & dbPath() const { return _dbPath; }
+      
+          /**
+           * @return Whether we are initialized.
+           **/
+          bool initialized() const { return( ! _root.empty() ); }
+      
+          /**
+           * Prepare access to the rpm database. Optional arguments may denote the
+           * root directory for all operations and the directory (below root) that
+           * contains the rpmdb (usg. you won't need to set this).
+           *
+           * On empty Pathnames the default is used:
+           * <PRE>
+           *     root:   /
+           *     dbPath: /var/lib/rpm
+           * </PRE>
+           *
+           * Calling initDatabase a second time with different arguments will return
+           * an error but leave the database in it's original state.
+           *
+           * Converting an old batabase is done if necessary. On update: The converted
+           * database will be removed by @ref closeDatabase, if it was not modified
+           * (no packages were installed or deleted). Otherwise the new database
+           * is kept, and the old one is removed.
+           **/
+          PMError initDatabase( Pathname root_r = Pathname(),
+                         Pathname dbPath_r = Pathname() );
+      
+          /**
+           * Block further access to the rpm database and go back to uninitialized
+           * state. On update: Decides what to do with any converted database
+           * (see @ref initDatabase).
+           **/
+          PMError closeDatabase();
+      
+          /**
+           * Rebuild the rpm database (rpm --rebuilddb).
+           **/
+          PMError rebuildDatabase();
+      
+          /**
+           * Import ascii armored public key in file pubkey_r.
+           **/
+          PMError importPubkey( const Pathname & pubkey_r );
+      
+          /**
+           * Import ascii armored public key keyname_r exported by keyring_r.
+           **/
+          PMError importPubkey( const Pathname & keyring_r, const std::string & keyname_r );
+      
+          /**
+           * Return the editions of all installed public keys.
+           **/
+          std::set<PkgEdition> pubkeys() const;
+      
+          ///////////////////////////////////////////////////////////////////
+          //
+          // Cached RPM database retrieval via librpm.
+          //
+          ///////////////////////////////////////////////////////////////////
+        private:
+      
+          class Packages;
+      
+          Packages & _packages;
+      
+          FileDeps::FileNames _filerequires;
+      
+        public:
+      
+          /**
+           * @return Whether the list of installed packages is valid, or
+           * you'd better reread it. (<B>NOTE:</B> returns valid, if not
+           * initialized).
+           **/
+          bool packagesValid() const;
+      
+          /**
+           * If necessary build, and return the list of all installed packages.
+           **/
+          const std::list<PMPackagePtr> & getPackages();
+      
+      
+          /**
+           * Hack to lookup required and conflicting file relations.
+           **/
+          void traceFileRel( const PkgRelation & rel_r );
+      
+          ///////////////////////////////////////////////////////////////////
+          //
+          // Direct RPM database retrieval via librpm.
+          //
+          ///////////////////////////////////////////////////////////////////
+        public:
+      
+          /**
+           * Return true if at least one package owns a certain file.
+           **/
+          bool hasFile( const std::string & file_r ) const;
+      
+          /**
+           * Return true if at least one package provides a certain tag.
+           **/
+          bool hasProvides( const std::string & tag_r ) const;
+      
+          /**
+           * Return true if at least one package requires a certain tag.
+           **/
+          bool hasRequiredBy( const std::string & tag_r ) const;
+      
+          /**
+           * Return true if at least one package conflicts with a certain tag.
+           **/
+          bool hasConflicts( const std::string & tag_r ) const;
+      
+          /**
+           * Return true if package is installed.
+           **/
+          bool hasPackage( const PkgName & name_r ) const;
+      
+          /**
+           * Get an installed packages data from rpmdb. Package is
+           * identified by name. Data returned via result are NULL,
+           * if packge is not installed (PMError is not set), or RPM database
+           * could not be read (PMError is set).
+           **/
+          PMError getData( const PkgName & name_r,
+                    constRpmHeaderPtr & result_r ) const;
+      
+          /**
+           * Get an installed packages data from rpmdb. Package is
+           * identified by name and edition. Data returned via result are NULL,
+           * if packge is not installed (PMError is not set), or RPM database
+           * could not be read (PMError is set).
+           **/
+          PMError getData( const PkgName & name_r, const PkgEdition & ed_r,
+                    constRpmHeaderPtr & result_r ) const;
+      
+          ///////////////////////////////////////////////////////////////////
+          //
+          ///////////////////////////////////////////////////////////////////
+        private:
+      
+          /**
+           * The connection to the rpm process.
+          */
+          ExternalProgram *process;
+      
+          typedef std::vector<const char*> RpmArgVec;
+      
+          /**
+           * Run rpm with the specified arguments and handle stderr.
+           * @param n_opts The number of arguments
+           * @param options Array of the arguments, @ref n_opts elements
+           * @param stderr_disp How to handle stderr, merged with stdout by default
+          */
+          void run_rpm( const RpmArgVec& options,
+                 ExternalProgram::Stderr_Disposition stderr_disp =
+                 ExternalProgram::Stderr_To_Stdout);
+      
+      
+          /**
+           * Read a line from the general rpm query
+          */
+          bool systemReadLine(std::string &line);
+      
+          /**
+           * Return the exit status of the general rpm process,
+           * closing the connection if not already done.
+          */
+          int systemStatus();
+      
+          /**
+           * Forcably kill the system process
+          */
+          void systemKill();
+      
+          /**
+           * The exit code of the rpm process, or -1 if not yet known.
+          */
+          int exit_code;
+      
+          /** /var/adm/backup */
+          Pathname _backuppath;
+      
+          /** create package backups? */
+          bool _packagebackups;
+      
+          /** whether <_root>/<WARNINGMAILPATH> was already created */
+          bool _warndirexists;
+      
+          /**
+           * handle rpm messages like "/etc/testrc saved as /etc/testrc.rpmorig"
+           *
+           * @param line rpm output starting with warning:
+           * @param name name of package, appears in subject line
+           * @param typemsg " saved as " or " created as "
+           * @param difffailmsg what to put into mail if diff failed, must contain two %s for the two files
+           * @param diffgenmsg what to put into mail if diff succeeded, must contain two %s for the two files
+           * */
+          void processConfigFiles(const std::string& line,
+                            const std::string& name,
+                            const char* typemsg,
+                            const char* difffailmsg,
+                            const char* diffgenmsg);
+      
+      
+        public:
+      
+          typedef std::set<std::string> FileList;
+      
+          /**
+           * Bits representing rpm installation options, useable as or
+           * combination
+           *
+           * @see installPackage(), removePackage()
+           * */
+          enum RpmInstFlag
+            {
+             RPMINST_NONE       = 0x0000,
+             RPMINST_NODOCS     = 0x0001,
+             RPMINST_NOSCRIPTS  = 0x0002,
+             RPMINST_FORCE      = 0x0004,
+             RPMINST_NODEPS     = 0x0008,
+             RPMINST_IGNORESIZE = 0x0010,
+             RPMINST_JUSTDB     = 0x0020,
+             RPMINST_NODIGEST   = 0x0040,
+             RPMINST_NOSIGNATURE= 0x0080,
+             RPMINST_NOUPGRADE  = 0x0100
+            };
+      
+          /**
+           * Bits of possible package corruptions
+           * @see checkPackage
+           * @see checkPackageResult2string
+           * */
+          enum checkPackageResult
+            {
+            CHK_OK                = 0x00,
+            CHK_INCORRECT_VERSION = 0x01, // package does not contain expected version
+            CHK_INCORRECT_FILEMD5 = 0x02, // md5sum of file is wrong (outside)
+            CHK_GPGSIG_MISSING    = 0x04, // package is not signeed
+            CHK_MD5SUM_MISSING    = 0x08, // package is not signeed
+            CHK_INCORRECT_GPGSIG  = 0x10, // signature incorrect
+            CHK_INCORRECT_PKGMD5  = 0x20, // md5sum incorrect (inside)
+            CHK_OTHER_FAILURE     = 0x40  // rpm failed for some reason
+            };
+      
+      
+          /**
+           * Check rpm with rpm --checksig
+           *
+           * @param filename which file to check
+           * @param version check if package really contains this version, leave emtpy to skip check
+           * @param md5 md5sum for whole file, leave empty to skip check (not yet implemented)
+           *
+           * @return checkPackageResult
+          */
+          unsigned checkPackage (const Pathname& filename, std::string version = "", std::string md5 = "" );
+      
+          /** install rpm package
+           *
+           * @param filename file to install
+           * @param flags which rpm options to use
+           *
+           * @return success
+           * */
+          PMError installPackage (const Pathname& filename, unsigned flags = 0 );
+      
+          /** remove rpm package
+           *
+           * @param name_r Name of the rpm package to remove.
+           * @param iflags which rpm options to use
+           *
+           * @return success
+           * */
+          PMError removePackage(const std::string & name_r, unsigned flags = 0);
+          PMError removePackage(constPMPackagePtr package, unsigned flags = 0);
+      
+          /**
+           * get backup dir for rpm config files
+           *
+           * */
+          Pathname getBackupPath (void) { return _backuppath; }
+      
+          /**
+           * create tar.gz of all changed files in a Package
+           *
+           * @param packageName name of the Package to backup
+           *
+           * @see setBackupPath
+           * */
+          bool backupPackage(const std::string& packageName);
+      
+          /**
+           * queries file for name and then calls above backupPackage
+           * function. For convenience.
+           *
+           * @param filename rpm file that is about to be installed
+           * */
+          bool backupPackage(const Pathname& filename);
+      
+          /**
+           * set path where package backups are stored
+           *
+           * @see backupPackage
+           * */
+          void setBackupPath(const Pathname& path);
+      
+          /**
+           * whether to create package backups during install or
+           * removal
+           *
+           * @param yes true or false
+           * */
+          void createPackageBackups(bool yes) { _packagebackups = yes; }
+      
+          /**
+           * determine which files of an installed package have been
+           * modified.
+           *
+           * @param fileList (output) where to store modified files
+           * @param packageName name of package to query
+           *
+           * @return false if package couln't be queried for some
+           * reason
+           * */
+          bool queryChangedFiles(FileList & fileList, const std::string& packageName);
+      
+        public: // static members
+      
+          /** create error description of bits set according to
+           * checkPackageResult
+           * */
+          static std::string checkPackageResult2string(unsigned code);
+      
+        public:
+      
+          /**
+           * Dump debug info.
+           **/
+          virtual std::ostream & dumpOn( std::ostream & str ) const;
+      
+          ///////////////////////////////////////////////////////////////////
+          //
+          // Installation log
+          //
+        ///////////////////////////////////////////////////////////////////
+        private:
+      
+          /**
+           * Progress of installation may be logged to file
+           **/
+          class Logfile;
+      
+        public:
+      
+          /**
+           * Set logfile for progress log. Empty filename to disable logging.
+           **/
+          static bool setInstallationLogfile( const Pathname & filename );
+      
+      };
+
+    } // namespace rpm
+  } // namespace target
+} // namespace zypp
+
+#endif // RpmDb_h
diff --git a/zypp/target/rpm/RpmHeader.cc b/zypp/target/rpm/RpmHeader.cc
new file mode 100644 (file)
index 0000000..7748f56
--- /dev/null
@@ -0,0 +1,720 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/RpmHeader.cc
+ *
+*/
+#include "librpm.h"
+
+#include <iostream>
+#include <map>
+#include <set>
+
+#include "zypp/base/Logger.h"
+#include "zypp/PathInfo.h"
+
+#include "zypp/target/rpm/RpmHeader.h"
+#include "zypp/CapFactory.h"
+#include "zypp/Rel.h"
+#warning FIXME this include
+#if 0
+#include <y2pm/PkgDu.h>
+#endif
+
+using namespace std;
+
+namespace zypp {
+  namespace target {
+    namespace rpm {
+
+      ///////////////////////////////////////////////////////////////////
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::RpmHeader
+      //        METHOD TYPE : Constructor
+      //
+      //        DESCRIPTION :
+      //
+      RpmHeader::RpmHeader( Header h_r )
+          : BinHeader( h_r )
+      {
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::RpmHeader
+      //        METHOD TYPE : Constructor
+      //
+      RpmHeader::RpmHeader( BinHeader::Ptr & rhs )
+          : BinHeader( rhs )
+      {
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::~RpmHeader
+      //        METHOD TYPE : Destructor
+      //
+      //        DESCRIPTION :
+      //
+      RpmHeader::~RpmHeader()
+      {
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::readPackage
+      //        METHOD TYPE : constRpmHeaderPtr
+      //
+      RpmHeader::constPtr RpmHeader::readPackage( const Pathname & path_r,
+                                         VERIFICATION verification_r )
+      {
+        PathInfo file( path_r );
+        if ( ! file.isFile() ) {
+          ERR << "Not a file: " << file << endl;
+          return (RpmHeader*)0;
+        }
+      
+        FD_t fd = ::Fopen( file.asString().c_str(), "r.ufdio" );
+        if ( fd == 0 || ::Ferror(fd) ) {
+          ERR << "Can't open file for reading: " << file << " (" << ::Fstrerror(fd) << ")" << endl;
+          if ( fd )
+            ::Fclose( fd );
+          return (RpmHeader*)0;
+        }
+      
+        rpmts ts = ::rpmtsCreate();
+        unsigned vsflag = RPMVSF_DEFAULT;
+        if ( verification_r & NODIGEST )
+          vsflag |= _RPMVSF_NODIGESTS;
+        if ( verification_r & NOSIGNATURE )
+          vsflag |= _RPMVSF_NOSIGNATURES;
+        ::rpmtsSetVSFlags( ts, rpmVSFlags(vsflag) );
+      
+        Header nh = 0;
+        int res = ::rpmReadPackageFile( ts, fd, path_r.asString().c_str(), &nh );
+      
+        ts = ::rpmtsFree(ts);
+      
+        ::Fclose( fd );
+      
+        if ( ! nh ) {
+          WAR << "Error reading header from " << path_r << " error(" << res << ")" << endl;
+          return (RpmHeader*)0;
+        }
+      
+        RpmHeader::constPtr h( new RpmHeader( nh ) );
+        headerFree( nh ); // clear the reference set in ReadPackageFile
+      
+        MIL << h << " from " << path_r << endl;
+        return h;
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::dumpOn
+      //        METHOD TYPE : ostream &
+      //
+      //        DESCRIPTION :
+      //
+      ostream & RpmHeader::dumpOn( ostream & str ) const
+      {
+        return BinHeader::dumpOn( str ) << '{' << tag_name() << "-" << tag_edition() << ( isSrc() ? ".src}" : "}");
+      }
+      
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::isSrc
+      //        METHOD TYPE : bool
+      //
+      bool RpmHeader::isSrc() const
+      {
+        return has_tag( RPMTAG_SOURCEPACKAGE );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_name
+      //        METHOD TYPE : string
+      //
+      //        DESCRIPTION :
+      //
+      string RpmHeader::tag_name() const
+      {
+        return string( string_val( RPMTAG_NAME ) );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_edition
+      //        METHOD TYPE : Edition
+      //
+      //        DESCRIPTION :
+      //
+      Edition RpmHeader::tag_edition() const
+      {
+        return Edition( string_val   ( RPMTAG_EPOCH ),
+                    string_val( RPMTAG_VERSION ),
+                    string_val( RPMTAG_RELEASE ) );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_arch
+      //        METHOD TYPE : Arch
+      //
+      //        DESCRIPTION :
+      //
+      Arch RpmHeader::tag_arch() const
+      {
+        return Arch( string_val( RPMTAG_ARCH ) );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_installtime
+      //        METHOD TYPE : Date
+      //
+      //        DESCRIPTION :
+      //
+      Date RpmHeader::tag_installtime() const
+      {
+        return int_val( RPMTAG_INSTALLTIME );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_buildtime
+      //        METHOD TYPE : Date
+      //
+      //        DESCRIPTION :
+      //
+      Date RpmHeader::tag_buildtime() const
+      {
+        return int_val( RPMTAG_BUILDTIME );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::PkgRelList_val
+      //        METHOD TYPE : unsigned
+      //
+      //        DESCRIPTION :
+      //
+      CapSet RpmHeader::PkgRelList_val( tag tag_r, bool pre, set<string> * freq_r ) const
+      {
+        CapSet ret;
+      
+        int_32  kindFlags   = 0;
+        int_32  kindVersion = 0;
+      
+        switch ( tag_r ) {
+        case RPMTAG_REQUIRENAME:
+          kindFlags   = RPMTAG_REQUIREFLAGS;
+          kindVersion = RPMTAG_REQUIREVERSION;
+          break;
+        case RPMTAG_PROVIDENAME:
+          kindFlags   = RPMTAG_PROVIDEFLAGS;
+          kindVersion = RPMTAG_PROVIDEVERSION;
+          break;
+        case RPMTAG_OBSOLETENAME:
+          kindFlags   = RPMTAG_OBSOLETEFLAGS;
+          kindVersion = RPMTAG_OBSOLETEVERSION;
+          break;
+        case RPMTAG_CONFLICTNAME:
+          kindFlags   = RPMTAG_CONFLICTFLAGS;
+          kindVersion = RPMTAG_CONFLICTVERSION;
+          break;
+        default:
+          INT << "Illegal RPMTAG_dependencyNAME " << tag_r << endl;
+         return ret;
+          break;
+        }
+      
+        stringList names;
+        unsigned count = string_list( tag_r, names );
+        if ( !count )
+          return ret;
+      
+        intList  flags;
+        int_list( kindFlags, flags );
+      
+        stringList versions;
+        string_list( kindVersion, versions );
+      
+        for ( unsigned i = 0; i < count; ++i ) {
+      
+          string n( names[i] );
+      
+          Rel op = Rel::ANY;
+          int_32 f  = flags[i];
+          string v  = versions[i];
+      
+          if ( n[0] == '/' ) {
+            if ( freq_r ) {
+              freq_r->insert( n );
+            }
+          } else {
+            if ( v.size() ) {
+              switch ( f & RPMSENSE_SENSEMASK ) {
+              case RPMSENSE_LESS:
+                op = Rel::LT;
+                break;
+              case RPMSENSE_LESS|RPMSENSE_EQUAL:
+                op = Rel::LE;
+                break;
+              case RPMSENSE_GREATER:
+                op = Rel::GT;
+                break;
+              case RPMSENSE_GREATER|RPMSENSE_EQUAL:
+                op = Rel::GE;
+                break;
+              case RPMSENSE_EQUAL:
+                op = Rel::EQ;
+                break;
+              }
+            }
+          }
+         if ((pre && (f & RPMSENSE_PREREQ))
+           || ((! pre) && !(f & RPMSENSE_PREREQ)))
+         {
+           CapFactory _f;
+           Capability cap = _f.parse(
+             ResTraits<Package>::kind,
+             n,
+             op,
+             Edition(v)
+           );
+           ret.insert(cap);
+         }
+        }
+      
+        return ret;
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_provides
+      //        METHOD TYPE : CapSet
+      //
+      //        DESCRIPTION :
+      //
+      CapSet RpmHeader::tag_provides( set<string> * freq_r ) const
+      {
+        return PkgRelList_val( RPMTAG_PROVIDENAME, false, freq_r );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_requires
+      //        METHOD TYPE : CapSet
+      //
+      //        DESCRIPTION :
+      //
+      CapSet RpmHeader::tag_requires( set<string> * freq_r ) const
+      {
+        return PkgRelList_val( RPMTAG_REQUIRENAME, false, freq_r );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_requires
+      //        METHOD TYPE : CapSet
+      //
+      //        DESCRIPTION :
+      //
+      CapSet RpmHeader::tag_prerequires( set<string> * freq_r ) const
+      {
+        return PkgRelList_val( RPMTAG_REQUIRENAME, true, freq_r );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_conflicts
+      //        METHOD TYPE : CapSet
+      //
+      //        DESCRIPTION :
+      //
+      CapSet RpmHeader::tag_conflicts( set<string> * freq_r ) const
+      {
+        return PkgRelList_val( RPMTAG_CONFLICTNAME, false, freq_r );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_obsoletes
+      //        METHOD TYPE : CapSet
+      //
+      //        DESCRIPTION :
+      //
+      CapSet RpmHeader::tag_obsoletes( set<string> * freq_r ) const
+      {
+        return PkgRelList_val( RPMTAG_OBSOLETENAME, false, freq_r );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_size
+      //        METHOD TYPE : ByteCount
+      //
+      //        DESCRIPTION :
+      //
+      ByteCount RpmHeader::tag_size() const
+      {
+        return int_val( RPMTAG_SIZE );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_archivesize
+      //        METHOD TYPE : ByteCount
+      //
+      //        DESCRIPTION :
+      //
+      ByteCount RpmHeader::tag_archivesize() const
+      {
+        return int_val( RPMTAG_ARCHIVESIZE );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_summary
+      //        METHOD TYPE : std::string
+      //
+      //        DESCRIPTION :
+      //
+      std::string RpmHeader::tag_summary() const
+      {
+        return string_val( RPMTAG_SUMMARY );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_description
+      //        METHOD TYPE : std::string
+      //
+      //        DESCRIPTION :
+      //
+      std::string RpmHeader::tag_description() const
+      {
+        return string_val( RPMTAG_DESCRIPTION );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_group
+      //        METHOD TYPE : std::string
+      //
+      //        DESCRIPTION :
+      //
+      std::string RpmHeader::tag_group() const
+      {
+        return string_val( RPMTAG_GROUP );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_vendor
+      //        METHOD TYPE : std::string
+      //
+      //        DESCRIPTION :
+      //
+      std::string RpmHeader::tag_vendor() const
+      {
+        return string_val( RPMTAG_VENDOR );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_distribution
+      //        METHOD TYPE : std::string
+      //
+      //        DESCRIPTION :
+      //
+      std::string RpmHeader::tag_distribution() const
+      {
+        return string_val( RPMTAG_DISTRIBUTION );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_license
+      //        METHOD TYPE : std::string
+      //
+      //        DESCRIPTION :
+      //
+      std::string RpmHeader::tag_license() const
+      {
+        return string_val( RPMTAG_LICENSE );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_buildhost
+      //        METHOD TYPE : std::string
+      //
+      //        DESCRIPTION :
+      //
+      std::string RpmHeader::tag_buildhost() const
+      {
+        return string_val( RPMTAG_BUILDHOST );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_packager
+      //        METHOD TYPE : std::string
+      //
+      //        DESCRIPTION :
+      //
+      std::string RpmHeader::tag_packager() const
+      {
+        return string_val( RPMTAG_PACKAGER );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_url
+      //        METHOD TYPE : std::string
+      //
+      //        DESCRIPTION :
+      //
+      std::string RpmHeader::tag_url() const
+      {
+        return string_val( RPMTAG_URL );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_os
+      //        METHOD TYPE : std::string
+      //
+      //        DESCRIPTION :
+      //
+      std::string RpmHeader::tag_os() const
+      {
+        return string_val( RPMTAG_OS );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_prein
+      //        METHOD TYPE : std::string
+      //
+      //        DESCRIPTION :
+      //
+      std::string RpmHeader::tag_prein() const
+      {
+        return string_val( RPMTAG_PREIN );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_postin
+      //        METHOD TYPE : std::string
+      //
+      //        DESCRIPTION :
+      //
+      std::string RpmHeader::tag_postin() const
+      {
+        return string_val( RPMTAG_POSTIN );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_preun
+      //        METHOD TYPE : std::string
+      //
+      //        DESCRIPTION :
+      //
+      std::string RpmHeader::tag_preun() const
+      {
+        return string_val( RPMTAG_PREUN );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_postun
+      //        METHOD TYPE : std::string
+      //
+      //        DESCRIPTION :
+      //
+      std::string RpmHeader::tag_postun() const
+      {
+        return string_val( RPMTAG_POSTUN );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_sourcerpm
+      //        METHOD TYPE : std::string
+      //
+      //        DESCRIPTION :
+      //
+      std::string RpmHeader::tag_sourcerpm() const
+      {
+        return string_val( RPMTAG_SOURCERPM );
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_filenames
+      //        METHOD TYPE : std::list<std::string>
+      //
+      //        DESCRIPTION :
+      //
+      std::list<std::string> RpmHeader::tag_filenames() const
+      {
+        std::list<std::string> ret;
+      
+        stringList basenames;
+        if ( string_list( RPMTAG_BASENAMES, basenames ) ) {
+          stringList dirnames;
+          string_list( RPMTAG_DIRNAMES, dirnames );
+          intList  dirindexes;
+          int_list( RPMTAG_DIRINDEXES, dirindexes );
+          for ( unsigned i = 0; i < basenames.size(); ++ i ) {
+            ret.push_back( dirnames[dirindexes[i]] + basenames[i] );
+          }
+        }
+      
+        return ret;
+      }
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_changelog
+      //        METHOD TYPE : Changelog
+      //
+      //        DESCRIPTION :
+      //
+      Changelog RpmHeader::tag_changelog() const
+      {
+        Changelog ret;
+      
+        intList times;
+        if ( int_list( RPMTAG_CHANGELOGTIME, times ) ) {
+          stringList names;
+          string_list( RPMTAG_CHANGELOGNAME, names );
+          stringList texts;
+          string_list( RPMTAG_CHANGELOGTEXT, texts );
+          for ( unsigned i = 0; i < times.size(); ++ i ) {
+            ret.push_back( ChangelogEntry( times[i], names[i], texts[i] ) );
+          }
+        }
+      
+        return ret;
+      }
+     
+#warning FIXME disk usage data
+#if 0 
+      ///////////////////////////////////////////////////////////////////
+      //
+      //
+      //        METHOD NAME : RpmHeader::tag_du
+      //        METHOD TYPE : PkgDu &
+      //
+      //        DESCRIPTION :
+      //
+      PkgDu & RpmHeader::tag_du( PkgDu & dudata_r ) const
+      {
+        dudata_r.clear();
+        stringList basenames;
+        if ( string_list( RPMTAG_BASENAMES, basenames ) ) {
+          stringList dirnames;
+          string_list( RPMTAG_DIRNAMES, dirnames );
+          intList dirindexes;
+          int_list( RPMTAG_DIRINDEXES, dirindexes );
+      
+          intList filedevices;
+          int_list( RPMTAG_FILEDEVICES, filedevices );
+          intList fileinodes;
+          int_list( RPMTAG_FILEINODES, fileinodes );
+          intList filesizes;
+          int_list( RPMTAG_FILESIZES, filesizes );
+          intList filemodes;
+          int_list( RPMTAG_FILEMODES, filemodes );
+      
+          ///////////////////////////////////////////////////////////////////
+          // Create and collect Entries by index. devino_cache is used to
+          // filter out hardliks ( different name but same device and inode ).
+          ///////////////////////////////////////////////////////////////////
+          PathInfo::devino_cache trace;
+          vector<PkgDu::Entry> entries;
+          entries.resize( dirnames.size() );
+          for ( unsigned i = 0; i < dirnames.size(); ++i ) {
+            entries[i] = dirnames[i];
+          }
+      
+          for ( unsigned i = 0; i < basenames.size(); ++ i ) {
+            PathInfo::stat_mode mode( filemodes[i] );
+            if ( mode.isFile() ) {
+              if ( trace.insert( filedevices[i], fileinodes[i] ) ) {
+                // Count full 1K blocks
+                entries[dirindexes[i]]._size += ByteCount( filesizes[i] ).fillBlock();
+                ++(entries[dirindexes[i]]._files);
+              }
+              // else: hardlink; already counted this device/inode
+            }
+          }
+      
+          ///////////////////////////////////////////////////////////////////
+          // Crreate and collect by index Entries. DevInoTrace is used to
+          // filter out hardliks ( different name but same device and inode ).
+          ///////////////////////////////////////////////////////////////////
+          for ( unsigned i = 0; i < entries.size(); ++i ) {
+            if ( entries[i]._size ) {
+              dudata_r.add( entries[i] );
+            }
+          }
+        }
+        return dudata_r;
+      }
+#endif
+
+    } // namespace rpm
+  } // namespace target
+} // namespace zypp
diff --git a/zypp/target/rpm/RpmHeader.h b/zypp/target/rpm/RpmHeader.h
new file mode 100644 (file)
index 0000000..ca48442
--- /dev/null
@@ -0,0 +1,164 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/RpmHeader.h
+ *
+*/
+#ifndef ZYPP_TARGET_RPM_RPMHEADER_H
+#define ZYPP_TARGET_RPM_RPMHEADER_H
+
+#include <iosfwd>
+#include <list>
+
+#include "zypp/target/rpm/BinHeader.h"
+
+#include "zypp/Package.h"
+#include "zypp/Changelog.h"
+#include "zypp/CapSetFwd.h"
+
+namespace zypp {
+  namespace target {
+    namespace rpm {
+
+#if 0
+      class PkgDu;
+#endif
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       CLASS NAME : RpmHeader
+      /**
+       * @short Wrapper class for rpm header struct.
+       *
+       * <code>RpmHeader</code> provides methods to query the content
+       * of a rpm header struct retrieved from the RPM database or by reading
+       * the rpm header of a package on disk.
+       *
+       * The rpm header contains all data associated with a package. So you
+       * probabely do not want to permanently store too many of them.
+       *
+       * <B>NEVER create <code>RpmHeader</code> from a NULL <code>Header</code>! </B>
+       **/
+      class RpmHeader : public BinHeader {
+        public:
+         typedef intrusive_ptr<RpmHeader> Ptr;
+         typedef intrusive_ptr<const RpmHeader> constPtr;
+
+        private:
+      
+          CapSet PkgRelList_val( tag tag_r, bool pre, std::set<std::string> * freq_r = 0 ) const;
+      
+        public:
+      
+          /**
+           *
+           **/
+          RpmHeader( Header h_r = 0 );
+      
+          /**
+           * <B>Dangerous!<\B> This one takes the header out of rhs
+           * and leaves rhs empty.
+           **/
+          RpmHeader( BinHeader::Ptr & rhs );
+      
+          virtual ~RpmHeader();
+      
+          bool isSrc() const;
+      
+        public:
+      
+          std::string    tag_name()        const;
+          Edition tag_edition()     const;
+          Arch    tag_arch()        const;
+      
+          Date       tag_installtime() const;
+          Date       tag_buildtime()   const;
+      
+          /**
+           * If <code>freq_r</code> is not NULL, file dependencies found are inserted.
+           **/
+          CapSet tag_provides ( std::set<std::string> * freq_r = 0 ) const;
+          /**
+           * @see #tag_provides
+           **/
+          CapSet tag_requires ( std::set<std::string> * freq_r = 0 ) const;
+          /**
+           * @see #tag_provides
+           **/
+          CapSet tag_prerequires ( std::set<std::string> * freq_r = 0 ) const;
+          /**
+           * @see #tag_provides
+           **/
+          CapSet tag_conflicts( std::set<std::string> * freq_r = 0 ) const;
+          /**
+           * @see #tag_provides
+           **/
+          CapSet tag_obsoletes( std::set<std::string> * freq_r = 0 ) const;
+      
+          ByteCount tag_size()        const;
+          ByteCount tag_archivesize() const;
+      
+          std::string tag_summary()      const;
+          std::string tag_description()  const;
+          std::string tag_group()        const;
+          std::string tag_vendor()       const;
+          std::string tag_distribution() const;
+          std::string tag_license()      const;
+          std::string tag_buildhost()    const;
+          std::string tag_packager()     const;
+          std::string tag_url()          const;
+          std::string tag_os()           const;
+          std::string tag_prein()        const;
+          std::string tag_postin()       const;
+          std::string tag_preun()        const;
+          std::string tag_postun()       const;
+          std::string tag_sourcerpm()    const;
+      
+          std::list<std::string> tag_filenames() const;
+      
+          Changelog tag_changelog() const;
+     
+#warning FIXME disk usage data
+#if 0 
+          /**
+           * Returns reference to arg <code>dudata_r</code>.
+           **/
+          PkgDu & tag_du( PkgDu & dudata_r ) const;
+#endif
+      
+        public:
+      
+          virtual std::ostream & dumpOn( std::ostream & str ) const;
+      
+        public:
+      
+          /**
+           * Digest and signature verification flags
+           **/
+          enum VERIFICATION {
+            VERIFY       = 0x0000,
+            NODIGEST     = (1<<0),
+            NOSIGNATURE  = (1<<1),
+            NOVERIFY     = 0xffff
+          };
+      
+          /**
+           * Get an accessible packages data from disk.
+           * Returns NULL on any error.
+           **/
+          static RpmHeader::constPtr readPackage( const Pathname & path,
+                                         VERIFICATION verification = VERIFY );
+      };
+
+///////////////////////////////////////////////////////////////////
+    } // namespace rpm
+  } // namespace target
+} // namespace zypp
+
+#endif // ZYPP_TARGET_RPM_RPMHEADER_H
+
diff --git a/zypp/target/rpm/RpmHeaderCache.cc b/zypp/target/rpm/RpmHeaderCache.cc
new file mode 100644 (file)
index 0000000..fde153b
--- /dev/null
@@ -0,0 +1,354 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/RpmHeaderCache.cc
+ *
+*/
+
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+
+#include "zypp/target/rpm/RpmHeaderCache.h"
+#include "zypp/target/rpm/RpmHeader.h"
+#include "librpm.h"
+#include "zypp/PathInfo.h"
+
+using namespace std;
+
+namespace zypp {
+  namespace target {
+    namespace rpm {
+
+///////////////////////////////////////////////////////////////////
+
+#warning add this function if needed
+#if 0
+const PkgNameEd & RpmHeaderCache::def_magic()
+{
+  static PkgNameEd _def_magic( PkgName("YaST-PHC"), PkgEdition("1.0-0") );
+  return _def_magic;
+}
+#endif
+
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmHeaderCache::RpmHeaderCache
+//     METHOD TYPE : Constructor
+//
+RpmHeaderCache::RpmHeaderCache( const Pathname & cache_r )
+    : BinHeaderCache( cache_r )
+{
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmHeaderCache::~RpmHeaderCache
+//     METHOD TYPE : Destructor
+//
+RpmHeaderCache::~RpmHeaderCache()
+{
+}
+
+#warning add this function if needed
+#if 0
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmHeaderCache::magicOk
+//     METHOD TYPE : bool
+//
+bool RpmHeaderCache::magicOk()
+{
+  PkgNameEd magic( PkgNameEd::fromString( _cmagic ) );
+  if ( magic != def_magic() ) {
+    ERR << "Found magic " << magic << ", expected " << def_magic() << endl;
+    return false;
+  }
+  DBG << "Found magic " << magic << endl;
+  return true;
+}
+#endif
+
+///////////////////////////////////////////////////////////////////
+#define RETURN_IF_CLOSED(R) if ( !isOpen() ) { ERR << "Cache not open: " << _cpath << endl; return R; }
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmHeaderCache::getFirst
+//     METHOD TYPE : constRpmHeaderPtr
+//
+RpmHeader::constPtr RpmHeaderCache::getFirst( Pathname & citem_r, int & isSource_r, pos & at_r )
+{
+  RETURN_IF_CLOSED( (RpmHeader*)0 );
+
+  if ( seek( _cheaderStart ) == npos ) {
+    ERR << "Can't seek to first header at " << _cheaderStart << endl;
+    return (RpmHeader*)0;
+  }
+
+  return getNext( citem_r, isSource_r, at_r );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmHeaderCache::getNext
+//     METHOD TYPE : constRpmHeaderPtr
+//
+RpmHeader::constPtr RpmHeaderCache::getNext( Pathname & citem_r, int & isSource_r, pos & at_r )
+{
+  RETURN_IF_CLOSED( RpmHeader::constPtr() );
+
+  static const unsigned sigsize = 8;
+
+  char sig[sigsize+1];
+  sig[sigsize] = '\0';
+
+  unsigned count = readData( sig, sigsize );
+  if ( count != sigsize ) {
+    if ( count ) {
+      ERR << "Error reading entry." << endl;
+    } // else EOF?
+    return (RpmHeader*)0;
+  }
+
+  if ( sig[0] != '@' || sig[sigsize-1] != '@' ) {
+    ERR << "Invalid entry." << endl;
+    return (RpmHeader*)0;
+  }
+
+  sig[sigsize-1] = '\0';
+  count = atoi( &sig[1] );
+
+  char citem[count+1];
+  citem[count] = '\0';
+
+  if ( readData( citem, count ) != count ) {
+    ERR << "Error reading entry data." << endl;
+    return (RpmHeader*)0;
+  }
+
+  isSource_r = ( citem[0] == 's' );
+  citem_r    = &citem[1];
+  at_r       = tell();
+
+  return getAt( at_r );
+}
+
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmHeaderCache::getAt
+//     METHOD TYPE : constRpmHeaderPtr
+//
+RpmHeader::constPtr RpmHeaderCache::getAt( pos at_r )
+{
+  RETURN_IF_CLOSED( RpmHeader::constPtr() );
+
+  if ( seek( at_r ) == npos ) {
+    ERR << "Can't seek to header at " << at_r << endl;
+    return (RpmHeader*)0;
+  }
+
+  BinHeader::Ptr bp = readHeader();
+  if ( !bp ) {
+    ERR << "Can't read header at " << at_r << endl;
+    return (RpmHeader*)0;
+  }
+
+  return new RpmHeader( bp );
+}
+
+/******************************************************************
+**
+**
+**     FUNCTION NAME : operator<<
+**     FUNCTION TYPE : ostream &
+*/
+ostream & operator<<( ostream & str, const RpmHeaderCache & obj )
+{
+  return str << "RpmHeaderCache@" << static_cast<const BinHeaderCache &>(obj);
+}
+
+///////////////////////////////////////////////////////////////////
+//
+#warning buildHeaderCache needs cleanup
+//
+///////////////////////////////////////////////////////////////////
+
+static const unsigned PHC_MAGIC_SZE = 64; // dont change!
+static const string   PHC_MAGIC( "YaST-PHC-1.0-0" );
+
+/******************************************************************
+**
+**
+**     FUNCTION NAME : phcAddMagic
+**     FUNCTION TYPE : void
+*/
+void phcAddMagic( FD_t fd )
+{
+  char magic[PHC_MAGIC_SZE];
+  memset( magic, 0, PHC_MAGIC_SZE );
+  strcpy( magic, PHC_MAGIC.c_str() );
+  strcpy( magic+PHC_MAGIC.size()+1, str::numstring( Date::now() ).c_str() );
+
+  ::Fwrite( magic, sizeof(char), PHC_MAGIC_SZE, fd );
+}
+
+/******************************************************************
+**
+**
+**     FUNCTION NAME : phcAddHeader
+**     FUNCTION TYPE : unsigned
+*/
+unsigned phcAddHeader( FD_t fd, Header h, const Pathname & citem_r, int isSource )
+{
+  string entry = str::form( "%c%s", (isSource?'s':'b'), citem_r.asString().c_str() );
+  entry = str::form( "@%6zu@%s", entry.size(), entry.c_str() );
+
+  ::Fwrite( entry.c_str(), sizeof(char), entry.size(), fd );
+  ::headerWrite( fd, h, HEADER_MAGIC_YES );
+
+  return 1;
+}
+
+/******************************************************************
+**
+**
+**     FUNCTION NAME : phcAddFile
+**     FUNCTION TYPE : unsigned
+*/
+unsigned phcAddFile( FD_t fd, const PathInfo & cpath_r, const Pathname & citem_r )
+{
+  FD_t pkg = ::Fopen( cpath_r.asString().c_str(), "r.ufdio" );
+  if ( pkg == 0 || ::Ferror(pkg) ) {
+    ERR << "Can't open file for reading: " << cpath_r << " (" << ::Fstrerror(pkg) << ")" << endl;
+    if ( pkg )
+      ::Fclose( pkg );
+    return 0;
+  }
+
+  rpmts ts = rpmtsCreate();
+  Header h = 0;
+  int res = ::rpmReadPackageFile( ts, pkg, cpath_r.path().asString().c_str(), &h );
+  ts = rpmtsFree(ts);
+  ::Fclose( pkg );
+
+  if ( ! h ) {
+    WAR << "Error reading header from " << cpath_r << " error(" << res << ")" << endl;
+    return 0;
+  }
+
+  RpmHeader::constPtr dummy( new RpmHeader( h ) ); // to handle header free
+  headerFree( h ); // clear reference set in ReadPackageFile
+  MIL << dummy << " for " << citem_r << endl;
+
+  return phcAddHeader( fd, h, citem_r, dummy->isSrc() );
+}
+
+/******************************************************************
+**
+**
+**     FUNCTION NAME : phcScanDir
+**     FUNCTION TYPE : unsigned
+*/
+unsigned phcScanDir( FD_t fd, const PathInfo & cpath_r, const Pathname & prfx_r,
+                    const RpmHeaderCache::buildOpts & options_r )
+{
+  DBG << "SCAN " << cpath_r << " (" << prfx_r << ")" << endl;
+
+  list<string> retlist;
+  int res = filesystem::readdir( retlist, cpath_r.path(), false );
+  if ( res ) {
+    ERR << "Error reading content of " << cpath_r << " (readdir " << res << ")" << endl;
+    return 0;
+  }
+
+  unsigned count = 0;
+  list<string> downlist;
+
+  for ( list<string>::const_iterator it = retlist.begin(); it != retlist.end(); ++it ) {
+    PathInfo cpath( cpath_r.path() + *it, PathInfo::LSTAT );
+    if ( cpath.isFile() ) {
+      count += phcAddFile( fd, cpath, prfx_r + *it );
+    } else if ( options_r.recurse && cpath.isDir() ) {
+      downlist.push_back( *it );
+    }
+  }
+  retlist.clear();
+
+  for ( list<string>::const_iterator it = downlist.begin(); it != downlist.end(); ++it ) {
+    count += phcScanDir( fd, PathInfo(cpath_r.path() + *it), prfx_r + *it, options_r );
+  }
+
+  return count;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : RpmHeaderCache::buildHeaderCache
+//     METHOD TYPE : int
+//
+int RpmHeaderCache::buildHeaderCache( const Pathname & cache_r,
+                                        const Pathname & pkgroot_r,
+                                        const buildOpts & options_r )
+{
+  ///////////////////////////////////////////////////////////////////
+  // Check pkgroot
+  ///////////////////////////////////////////////////////////////////
+
+  PathInfo pkgroot( pkgroot_r );
+  if ( !pkgroot.isDir() ) {
+    ERR << "Not a directory: Pkgroot " << pkgroot << endl;
+    return -1;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  // Prepare cache file
+  ///////////////////////////////////////////////////////////////////
+  FD_t fd = ::Fopen( cache_r.asString().c_str(), "w"  );
+  if ( fd == 0 || ::Ferror(fd) ) {
+    ERR << "Can't open cache for writing: " << cache_r << " (" << ::Fstrerror(fd) << ")" << endl;
+    if ( fd )
+      ::Fclose( fd );
+    return -2;
+  }
+
+  phcAddMagic( fd );
+
+  ///////////////////////////////////////////////////////////////////
+  // Scan pkgroot_r
+  ///////////////////////////////////////////////////////////////////
+  MIL << "Start scan below " << pkgroot_r
+    << " (recurse=" << (options_r.recurse?"yes":"no")
+    << ")" << endl;
+  unsigned count = phcScanDir( fd, pkgroot, "/", options_r );
+
+  if ( ::Ferror(fd) ) {
+    ERR << "Error writing cache: " << cache_r << " (" << ::Fstrerror(fd) << ")" << endl;
+    ::Fclose( fd );
+    return -3;
+  }
+
+  MIL << "Found " << count << " package(s) below " << pkgroot_r << endl;
+  ::Fclose( fd );
+  return count;
+}
+
+    } // namespace rpm
+  } // namespace target
+} // namespace zypp
diff --git a/zypp/target/rpm/RpmHeaderCache.h b/zypp/target/rpm/RpmHeaderCache.h
new file mode 100644 (file)
index 0000000..6839c3a
--- /dev/null
@@ -0,0 +1,77 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/RpmHeaderCache.h
+ *
+*/
+#ifndef ZYPP_TARGET_RPM_RPMHEADERCACHE_H
+#define ZYPP_TARGET_RPM_RPMHEADERCACHE_H
+
+#include <iosfwd>
+
+#include "zypp/Pathname.h"
+#include "zypp/target/rpm/BinHeaderCache.h"
+#include "zypp/target/rpm/RpmHeader.h"
+
+namespace zypp {
+  namespace target {
+    namespace rpm {
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //        CLASS NAME : RpmHeaderCache
+      /**
+       *
+       **/
+      class RpmHeaderCache : public BinHeaderCache {
+      
+        friend std::ostream & operator<<( std::ostream & str, const RpmHeaderCache & obj );
+      
+        RpmHeaderCache & operator=( const RpmHeaderCache & );
+        RpmHeaderCache            ( const RpmHeaderCache & );
+      
+        private:
+#warning Add this function if it is needed
+#if 0      
+          static const PkgNameEd & def_magic();
+      
+        protected:
+      
+          virtual bool magicOk();
+#endif
+      
+        public:
+      
+          RpmHeaderCache( const Pathname & cache_r );
+          virtual ~RpmHeaderCache();
+      
+          RpmHeader::constPtr getFirst( Pathname & citem_r, int & isSource_r, pos & at_r );
+          RpmHeader::constPtr getNext( Pathname & citem_r, int & isSource_r, pos & at_r );
+      
+          RpmHeader::constPtr getAt( pos at_r );
+      
+        public:
+      
+          struct buildOpts {
+            bool recurse;
+            buildOpts()
+              : recurse( false )
+            {}
+          };
+      
+          static int buildHeaderCache( const Pathname & cache_r, const Pathname & pkgroot_r,
+                                const buildOpts & options_r = buildOpts() );
+      };
+      
+      ///////////////////////////////////////////////////////////////////
+      
+    } // namespace rpm
+  } // namespace target
+} // namespace zypp
+
+#endif // ZYPP_TARGET_RPM_RPMHEADERCACHE_H
diff --git a/zypp/target/rpm/librpm.h b/zypp/target/rpm/librpm.h
new file mode 100644 (file)
index 0000000..d330901
--- /dev/null
@@ -0,0 +1,32 @@
+/*---------------------------------------------------------------------\
+|                                                                      |
+|                      __   __    ____ _____ ____                      |
+|                      \ \ / /_ _/ ___|_   _|___ \                     |
+|                       \ V / _` \___ \ | |   __) |                    |
+|                        | | (_| |___) || |  / __/                     |
+|                        |_|\__,_|____/ |_| |_____|                    |
+|                                                                      |
+|                               core system                            |
+|                                                        (C) SuSE GmbH |
+\----------------------------------------------------------------------/
+
+  File:       librpm.h
+
+  Author:     Michael Andres <ma@suse.de>
+  Maintainer: Michael Andres <ma@suse.de>
+
+  Purpose:
+
+/-*/
+#ifndef librpm_h
+#define librpm_h
+
+extern "C" {
+#include <rpm/rpmlib.h>
+#include <rpm/rpmmacro.h>
+#include <rpm/rpmdb.h>
+#include <rpm/rpmts.h>
+#include <fcntl.h>
+}
+
+#endif // librpm_h
diff --git a/zypp/target/rpm/librpmDb.cc b/zypp/target/rpm/librpmDb.cc
new file mode 100644 (file)
index 0000000..fc0d122
--- /dev/null
@@ -0,0 +1,864 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/BinHeader.h
+ *
+*/
+#include "librpm.h"
+
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/target/rpm/librpmDb.h"
+#include "zypp/target/rpm/RpmHeader.h"
+#include "zypp/base/Exception.h"
+
+using namespace std;
+
+namespace zypp {
+  namespace target {
+    namespace rpm {
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : librpmDb::D
+/**
+ * @short librpmDb internal database handle
+ **/
+class librpmDb::D {
+  D & operator=( const D & ); // NO ASSIGNMENT!
+  D ( const D & );            // NO COPY!
+  public:
+
+    const Pathname _root;   // root directory for all operations
+    const Pathname _dbPath; // directory (below root) that contains the rpmdb
+    rpmdb          _db;     // database handle
+#warning fix string representation of the error
+    shared_ptr<Exception> _error;  // database error
+
+    friend ostream & operator<<( ostream & str, const D & obj ) {
+      str << "{" << obj._error  << "(" << obj._root << ")" << obj._dbPath << "}";
+      return str;
+    }
+
+    D( const Pathname & root_r, const Pathname & dbPath_r, bool readonly_r )
+      : _root  ( root_r )
+      , _dbPath( dbPath_r )
+      , _db    ( 0 )
+    {
+      _error.reset();
+      // set %_dbpath macro
+      ::addMacro( NULL, "_dbpath", NULL, _dbPath.asString().c_str(), RMIL_CMDLINE );
+      const char * root = ( _root == "/" ? NULL : _root.asString().c_str() );
+      int          perms = 0644;
+
+      // check whether to create a new db
+      PathInfo master( _root + _dbPath + "Packages" );
+      if ( ! master.isFile() ) {
+       // init database
+       int res = ::rpmdbInit( root, perms );
+       if ( res ) {
+         _error = shared_ptr<Exception>(new Exception("Error::E_RpmDB_init_failed"));
+         ERR << "rpmdbInit error(" << res << "): " << *this << endl;
+         return;
+       }
+      }
+
+      // open database
+      int res = ::rpmdbOpen( root, &_db, (readonly_r ? O_RDONLY : O_RDWR ), perms );
+      if ( res || !_db ) {
+       _error = shared_ptr<Exception>(new Exception("Error::E_RpmDB_open_failed"));
+       if ( _db ) {
+         ::rpmdbClose( _db );
+         _db = 0;
+       }
+       ERR << "rpmdbOpen error(" << res << "): " << *this << endl;
+       return;
+      }
+
+      DBG << "DBACCESS " << *this << endl;
+    }
+
+    ~D() {
+      if ( _db ) {
+#if 0
+       // login here may cause a SEGV, if call is caused by
+       // static variables being deleted. Might be that PMError
+       // static strings or logstreams are already destructed.
+       int res = ::rpmdbClose( _db );
+       if ( res ) {
+         WAR << "::rpmdbClose error(" << res << ")" << endl;
+       }
+       DBG << "DBCLOSE " << *this << endl;
+#else
+      ::rpmdbClose( _db );
+#endif
+      }
+    }
+};
+
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : librpmDb (ststic interface)
+//
+///////////////////////////////////////////////////////////////////
+
+Pathname         librpmDb::_defaultRoot  ( "/" );
+Pathname         librpmDb::_defaultDbPath( "/var/lib/rpm" );
+librpmDb::constPtr librpmDb::_defaultDb;
+bool             librpmDb::_dbBlocked    ( true );
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::globalInit
+//     METHOD TYPE : bool
+//
+bool librpmDb::globalInit()
+{
+  static bool initialized = false;
+
+  if ( initialized )
+    return true;
+
+  int rc = ::rpmReadConfigFiles( NULL, NULL );
+  if ( rc ) {
+    ERR << "rpmReadConfigFiles returned " << rc << endl;
+    return false;
+  }
+
+  // should speed up convertdb and rebuilddb.
+  ::addMacro( NULL, "_rpmdb_rebuild", NULL, "%{nil}", RMIL_CMDLINE );
+
+  initialized = true; // Necessary to be able to use exand().
+
+#define OUTVAL(n) << " (" #n ":" << expand( "%{" #n "}" ) << ")"
+  MIL << "librpm init done:"
+    OUTVAL(_target)
+    OUTVAL(_dbpath)
+      << endl;
+#undef OUTVAL
+  return initialized;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::expand
+//     METHOD TYPE : std::string
+//
+std::string librpmDb::expand( const std::string & macro_r )
+{
+  if ( ! globalInit() )
+    return macro_r;  // unexpanded
+
+  char * val = ::rpmExpand( macro_r.c_str(), NULL );
+  if ( !val )
+    return "";
+
+  string ret( val );
+  free( val );
+  return ret;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::newLibrpmDb
+//     METHOD TYPE : librpmDb *
+//
+librpmDb * librpmDb::newLibrpmDb( Pathname root_r, Pathname dbPath_r, bool readonly_r )
+{
+  // check arguments
+  if ( ! (root_r.absolute() && dbPath_r.absolute()) ) {
+    ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
+    ZYPP_THROW(Exception("Error::E_invalid_argument"));
+  }
+
+  // initialize librpm
+  if ( ! globalInit() ) {
+    ZYPP_THROW(Exception("Error::E_RpmDB_global_init_failed"));
+  }
+
+  // open rpmdb
+  librpmDb * ret = new librpmDb( root_r, dbPath_r, readonly_r );
+  shared_ptr<Exception> err_r = ret->_d._error;
+  if ( err_r ) {
+    delete ret;
+    ret = 0;
+    ZYPP_THROW(ret->_d._error);
+  }
+  return ret;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::dbAccess
+//     METHOD TYPE : PMError
+//
+void librpmDb::dbAccess( const Pathname & root_r, const Pathname & dbPath_r )
+{
+  // check arguments
+  if ( ! (root_r.absolute() && dbPath_r.absolute()) ) {
+    ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
+    ZYPP_THROW(Exception("Error::E_invalid_argument"));
+  }
+
+  if ( _defaultDb ) {
+    // already accessing a database: switching is not allowed.
+    if ( _defaultRoot == root_r && _defaultDbPath == dbPath_r )
+      return;
+    else {
+      ERR << "Can't switch to " << stringPath( root_r, dbPath_r )
+       << " while accessing " << stringPath( _defaultRoot, _defaultDbPath ) << endl;
+      ZYPP_THROW(Exception("Error::E_RpmDB_already_open"));
+    }
+  }
+
+  // got no database: we could switch to a new one (even if blocked!)
+  _defaultRoot = root_r;
+  _defaultDbPath = dbPath_r;
+  MIL << "Set new database location: " << stringPath( _defaultRoot, _defaultDbPath ) << endl;
+
+  return dbAccess();
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::dbAccess
+//     METHOD TYPE : PMError
+//
+void librpmDb::dbAccess()
+{
+  if ( _dbBlocked ) {
+    WAR << "Access is blocked: " << stringPath( _defaultRoot, _defaultDbPath ) << endl;
+    ZYPP_THROW(Exception("Error::E_RpmDB_access_blocked"));
+  }
+
+  if ( !_defaultDb ) {
+    // get access
+    _defaultDb = newLibrpmDb( _defaultRoot, _defaultDbPath, /*readonly*/true );
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::dbAccess
+//     METHOD TYPE : PMError
+//
+void librpmDb::dbAccess( librpmDb::constPtr & ptr_r )
+{
+  try {
+    dbAccess();
+  }
+  catch (Exception & excpt_r)
+  {
+    ptr_r = 0;
+    ZYPP_RETHROW(excpt_r);
+  }
+  ptr_r = _defaultDb;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::dbRelease
+//     METHOD TYPE : unsigned
+//
+unsigned librpmDb::dbRelease( bool force_r )
+{
+  if ( !_defaultDb ) {
+    return 0;
+  }
+
+  unsigned outstanding = _defaultDb->refCount() - 1; // refCount can't be 0
+
+  switch ( outstanding ) {
+  default:
+    if ( !force_r ) {
+      DBG << "dbRelease: keep access, outstanding " << outstanding << endl;
+      break;
+    }
+    // else fall through:
+  case 0:
+    DBG << "dbRelease: release" << (force_r && outstanding ? "(forced)" : "")
+      << ", outstanding " << outstanding << endl;
+
+    _defaultDb->_d._error = shared_ptr<Exception>(new Exception("Error::E_RpmDB_access_blocked")); // tag handle invalid
+    _defaultDb = 0;
+    break;
+  }
+
+  return outstanding;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::blockAccess
+//     METHOD TYPE : unsigned
+//
+unsigned librpmDb::blockAccess()
+{
+  MIL << "Block access" << endl;
+  _dbBlocked = true;
+  return dbRelease( /*force*/true );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::unblockAccess
+//     METHOD TYPE : void
+//
+void librpmDb::unblockAccess()
+{
+  MIL << "Unblock access" << endl;
+  _dbBlocked = false;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::dumpState
+//     METHOD TYPE : ostream &
+//
+ostream & librpmDb::dumpState( ostream & str )
+{
+  if ( !_defaultDb ) {
+    return str << "[librpmDb " << (_dbBlocked?"BLOCKED":"CLOSED") << " " << stringPath( _defaultRoot, _defaultDbPath ) << "]";
+  }
+  return str << "[" << _defaultDb << "]";
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : librpmDb (internal database handle interface (nonstatic))
+//
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::librpmDb
+//     METHOD TYPE : Constructor
+//
+//     DESCRIPTION :
+//
+librpmDb::librpmDb( const Pathname & root_r, const Pathname & dbPath_r, bool readonly_r )
+    : _d( * new D( root_r, dbPath_r, readonly_r ) )
+{
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::~librpmDb
+//     METHOD TYPE : Destructor
+//
+//     DESCRIPTION :
+//
+librpmDb::~librpmDb()
+{
+  delete &_d;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::unref_to
+//     METHOD TYPE : void
+//
+void librpmDb::unref_to( unsigned refCount_r ) const
+{
+  if ( refCount_r == 1 ) {
+    dbRelease();
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::root
+//     METHOD TYPE : const Pathname &
+//
+const Pathname & librpmDb::root() const
+{
+  return _d._root;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::dbPath
+//     METHOD TYPE : const Pathname &
+//
+const Pathname & librpmDb::dbPath() const
+{
+  return _d._dbPath;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::error
+//     METHOD TYPE : PMError
+//
+PMError librpmDb::error() const
+{
+  return _d._error;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::empty
+//     METHOD TYPE : bool
+//
+bool librpmDb::empty() const
+{
+  return( valid() && ! *db_const_iterator( this ) );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::dont_call_it
+//     METHOD TYPE : void *
+//
+void * librpmDb::dont_call_it() const
+{
+  return _d._db;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::dumpOn
+//     METHOD TYPE : ostream &
+//
+//     DESCRIPTION :
+//
+ostream & librpmDb::dumpOn( ostream & str ) const
+{
+  ReferenceCounted::dumpOn( str ) << _d;
+  return str;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : librpmDb::DbDirInfo
+//
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::DbDirInfo::DbDirInfo
+//     METHOD TYPE : Constructor
+//
+librpmDb::DbDirInfo::DbDirInfo( const Pathname & root_r, const Pathname & dbPath_r )
+    : _root( root_r )
+    , _dbPath( dbPath_r )
+{
+  // check and adjust arguments
+  if ( ! (root_r.absolute() && dbPath_r.absolute()) ) {
+    ERR << "Relative path for root(" << _root << ") or dbPath(" << _dbPath << ")" << endl;
+  } else {
+    _dbDir   ( _root + _dbPath );
+    _dbV4    ( _dbDir.path() + "Packages" );
+    _dbV3    ( _dbDir.path() + "packages.rpm" );
+    _dbV3ToV4( _dbDir.path() + "packages.rpm3" );
+    DBG << *this << endl;
+  }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::DbDirInfo::update
+//     METHOD TYPE : void
+//
+void librpmDb::DbDirInfo::restat()
+{
+  _dbDir();
+  _dbV4();
+  _dbV3();
+  _dbV3ToV4();
+  DBG << *this << endl;
+}
+
+/******************************************************************
+**
+**
+**     FUNCTION NAME : operator<<
+**     FUNCTION TYPE : std::ostream &
+*/
+std::ostream & operator<<( std::ostream & str, const librpmDb::DbDirInfo & obj )
+{
+  if ( obj.illegalArgs() ) {
+    str << "ILLEGAL: '(" << obj.root() << ")" << obj.dbPath() << "'";
+  } else {
+    str << "'(" << obj.root() << ")" << obj.dbPath() << "':" << endl;
+    str << "  Dir:    " << obj._dbDir << endl;
+    str << "  V4:     " << obj._dbV4 << endl;
+    str << "  V3:     " << obj._dbV3 << endl;
+    str << "  V3ToV4: " << obj._dbV3ToV4;
+  }
+  return str;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : librpmDb::db_const_iterator::D
+/**
+ *
+ **/
+class librpmDb::db_const_iterator::D {
+  D & operator=( const D & ); // NO ASSIGNMENT!
+  D ( const D & );            // NO COPY!
+  public:
+
+    librpmDb::constPtr     _dbptr;
+#warning use better type
+    string              _dberr;
+
+    RpmHeader::constPtr _hptr;
+    rpmdbMatchIterator   _mi;
+
+    D( librpmDb::constPtr dbptr_r )
+      : _dbptr( dbptr_r )
+      , _mi( 0 )
+    {
+      if ( !_dbptr ) {
+       // try to get librpmDb's default db
+       _dberr = librpmDb::dbAccess( _dbptr );
+       if ( !_dbptr ) {
+         WAR << "No database access: " << _dberr << endl;
+       }
+      } else {
+       destroy(); // Checks whether _dbptr still valid
+      }
+    }
+
+    ~D() {
+      if ( _mi ) {
+       ::rpmdbFreeIterator( _mi );
+      }
+    }
+
+    /**
+     * Let iterator access a dbindex file. Call @ref advance to access the
+     * 1st element (if present).
+     **/
+    bool create( int rpmtag, const void * keyp = NULL, size_t keylen = 0 ) {
+      destroy();
+      if ( ! _dbptr )
+       return false;
+      _mi = ::rpmdbInitIterator( _dbptr->_d._db, rpmTag(rpmtag), keyp, keylen );
+      return _mi;
+    }
+
+    /**
+     * Destroy iterator. Invalidates _dbptr, if database was blocked meanwile.
+     * Always returns false.
+     **/
+    bool destroy() {
+      if ( _mi ) {
+       _mi = ::rpmdbFreeIterator( _mi );
+       _hptr = 0;
+      }
+      if ( _dbptr && _dbptr->error() ) {
+       _dberr = _dbptr->error();
+       WAR << "Lost database access: " << _dberr << endl;
+       _dbptr = 0;
+      }
+      return false;
+    }
+
+    /**
+     * Advance to the first/next header in iterator. Destroys iterator if
+     * no more headers available.
+     **/
+    bool advance() {
+      if ( !_mi )
+       return false;
+      Header h = ::rpmdbNextIterator( _mi );
+      if ( ! h ) {
+       destroy();
+       return false;
+      }
+      _hptr = new RpmHeader( h );
+      return true;
+    }
+
+    /**
+     * Access a dbindex file and advance to the 1st header.
+     **/
+    bool init( int rpmtag, const void * keyp = NULL, size_t keylen = 0 ) {
+      if ( ! create( rpmtag, keyp, keylen ) )
+       return false;
+      return advance();
+    }
+
+    /**
+     * Create an itertator that contains the database entry located at
+     * off_r, and advance to the 1st header.
+     **/
+    bool set( int off_r ) {
+      if ( ! create( RPMDBI_PACKAGES ) )
+       return false;
+#warning TESTCASE: rpmdbAppendIterator and (non)sequential access?
+      ::rpmdbAppendIterator( _mi, &off_r, 1 );
+      return advance();
+    }
+
+    unsigned offset() {
+      return( _mi ? ::rpmdbGetIteratorOffset( _mi ) : 0 );
+    }
+
+    int size() {
+      if ( !_mi )
+       return 0;
+      int ret = ::rpmdbGetIteratorCount( _mi );
+#warning TESTCASE: rpmdbGetIteratorCount returns 0 on sequential access?
+      return( ret ? ret : -1 ); // -1: sequential access
+    }
+};
+
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : librpmDb::Ptr::db_const_iterator
+//
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::db_iterator
+//     METHOD TYPE : Constructor
+//
+librpmDb::db_const_iterator::db_const_iterator( librpmDb::constPtr dbptr_r )
+    : _d( * new D( dbptr_r ) )
+{
+  findAll();
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::~db_const_iterator
+//     METHOD TYPE : Destructor
+//
+librpmDb::db_const_iterator::~db_const_iterator()
+{
+  delete &_d;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::operator++
+//     METHOD TYPE : void
+//
+void librpmDb::db_const_iterator::operator++()
+{
+  _d.advance();
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::dbHdrNum
+//     METHOD TYPE : unsigned
+//
+unsigned librpmDb::db_const_iterator::dbHdrNum() const
+{
+  return _d.offset();
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::operator*
+//     METHOD TYPE : const RpmHeader::constPtr &
+//
+const RpmHeader::constPtr & librpmDb::db_const_iterator::operator*() const
+{
+  return _d._hptr;
+}
+
+#warning FIXME this function
+#if 0
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::dbError
+//     METHOD TYPE : PMError
+//
+PMError librpmDb::db_const_iterator::dbError() const
+{
+  if ( _d._dbptr )
+    return _d._dbptr->error();
+
+  return _d._dberr;
+}
+#endif
+
+/******************************************************************
+**
+**
+**     FUNCTION NAME : operator<<
+**     FUNCTION TYPE : ostream &
+*/
+ostream & operator<<( ostream & str, const librpmDb::db_const_iterator & obj )
+{
+  str << "db_const_iterator(" << obj._d._dbptr
+    << " Size:" << obj._d.size()
+      << " HdrNum:" << obj._d.offset()
+       << ")";
+  return str;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::findAll
+//     METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findAll()
+{
+  return _d.init( RPMDBI_PACKAGES );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::findByFile
+//     METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findByFile( const std::string & file_r )
+{
+  return _d.init( RPMTAG_BASENAMES, file_r.c_str() );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::findByProvides
+//     METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findByProvides( const std::string & tag_r )
+{
+  return _d.init( RPMTAG_PROVIDENAME, tag_r.c_str() );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::findByRequiredBy
+//     METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findByRequiredBy( const std::string & tag_r )
+{
+  return _d.init( RPMTAG_REQUIRENAME, tag_r.c_str() );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::findByConflicts
+//     METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findByConflicts( const std::string & tag_r )
+{
+  return _d.init( RPMTAG_CONFLICTNAME, tag_r.c_str() );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::findByName
+//     METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findByName( const string & name_r )
+{
+  return _d.init( RPMTAG_NAME, name_r.c_str() );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::findPackage
+//     METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findPackage( const string & name_r )
+{
+  if ( ! _d.init( RPMTAG_NAME, name_r.c_str() ) )
+    return false;
+
+  if ( _d.size() == 1 )
+    return true;
+
+  // check installtime on multiple entries
+  int match    = 0;
+  time_t itime = 0;
+  for ( ; operator*(); operator++() ) {
+    if ( operator*()->tag_installtime() > itime ) {
+      match = _d.offset();
+      itime = operator*()->tag_installtime();
+    }
+  }
+
+  return _d.set( match );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::findPackage
+//     METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findPackage( const std::string & name_r, const Edition & ed_r )
+{
+  if ( ! _d.init( RPMTAG_NAME, name_r.c_str() ) )
+    return false;
+
+  for ( ; operator*(); operator++() ) {
+    if ( ed_r == operator*()->tag_edition() ) {
+      int match = _d.offset();
+      return _d.set( match );
+    }
+  }
+
+  return _d.destroy();
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+//     METHOD NAME : librpmDb::db_const_iterator::findPackage
+//     METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findPackage( const Package::constPtr & which_r )
+{
+  if ( ! which_r )
+    return _d.destroy();
+
+  return findPackage( which_r->name(), which_r->edition() );
+}
+
+    } // namespace rpm
+  } // namespace target
+} // namespace zypp
diff --git a/zypp/target/rpm/librpmDb.h b/zypp/target/rpm/librpmDb.h
new file mode 100644 (file)
index 0000000..f0424e3
--- /dev/null
@@ -0,0 +1,589 @@
+/*---------------------------------------------------------------------\
+|                                                                      |
+|                      __   __    ____ _____ ____                      |
+|                      \ \ / /_ _/ ___|_   _|___ \                     |
+|                       \ V / _` \___ \ | |   __) |                    |
+|                        | | (_| |___) || |  / __/                     |
+|                        |_|\__,_|____/ |_| |_____|                    |
+|                                                                      |
+|                               core system                            |
+|                                                        (C) SuSE GmbH |
+\----------------------------------------------------------------------/
+
+  File:       librpmDb.h
+
+  Author:     Michael Andres <ma@suse.de>
+  Maintainer: Michael Andres <ma@suse.de>
+
+  Purpose: Manage access to librpm database.
+
+/-*/
+#ifndef librpmDb_h
+#define librpmDb_h
+
+#include <iosfwd>
+
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/PathInfo.h"
+#include "zypp/Package.h"
+#include "zypp/target/rpm/RpmHeader.h"
+
+namespace zypp {
+  namespace target {
+    namespace rpm {
+
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       CLASS NAME : librpmDb
+      /**
+       * @short Manage access to librpm database.
+       **/
+      class librpmDb : public base::ReferenceCounted, private base::NonCopyable 
+      {
+        public:
+         typedef intrusive_ptr<librpmDb> Ptr;
+         typedef intrusive_ptr<const librpmDb> constPtr;
+        private:
+        /**
+         * <B>INTENTIONALLY UNDEFINED<\B> because of bug in Ptr classes
+         * which allows implicit conversion from librpmDb::Ptr to
+         * librpmDb::constPtr. Currently we don't want to provide non const
+         * handles, as the database is opened READONLY.
+        *
+         * \throws Exception
+         *
+         **/
+        static void dbAccess( librpmDb::Ptr & ptr_r );
+      
+        public:
+      
+          ///////////////////////////////////////////////////////////////////
+          //
+          //   static interface
+          //
+          ///////////////////////////////////////////////////////////////////
+        private:
+      
+          /**
+           * Current root directory for all operations.
+           * (initialy /)
+           **/
+          static Pathname _defaultRoot;
+      
+          /**
+           * Current directory (below root) that contains the rpmdb.
+           * (initialy /var/lib/rpm)
+           **/
+          static Pathname _defaultDbPath;
+      
+          /**
+           * Current rpmdb handle.
+           **/
+          static librpmDb::constPtr _defaultDb;
+      
+          /**
+           * Wheter access is blocked (no _defaultDb will be available).
+           **/
+          static bool _dbBlocked;
+      
+          /**
+           * For internal use. Pointer returned should immediately be
+           * wrapped into librpmDb::Ptr.
+          *
+           * \throws Exception
+           *
+           **/
+          static librpmDb * newLibrpmDb( Pathname root_r, Pathname dbPath_r, bool readonly_r );
+      
+          /**
+           * Access the database at the current default location. If necessary
+           * (eg. after @ref dbRelease), the database is opened. This just creates
+           * the internal handle. Once the handle is passed to e.g. some
+           * @ref db_const_iterator, the database will be closed if the last
+           * outstanding reference goes out of scope. If no external reference is
+           * created, you'll have to explicitly call @ref dbRelease to close the
+           * database.
+          *
+           * \throws Exception
+           *
+           **/
+          static void dbAccess();
+      
+        public:
+      
+          /**
+           * Initialize lib librpm (read configfiles etc.). It's called
+           * on demand but you may call it anytime.
+           *
+           * @return Whether librpm is initialized.
+           **/
+          static bool globalInit();
+      
+          /**
+           * @return librpm macro expansion.
+           **/
+          static std::string expand( const std::string & macro_r );
+      
+          /**
+           * @return String '(root_r)sub_r' used in debug output.
+           **/
+          static std::string stringPath( const Pathname & root_r, const Pathname & sub_r ) {
+            return std::string( "'(" ) + root_r.asString() + ")" + sub_r.asString() + "'";
+          }
+      
+        public:
+      
+          /**
+           * @return Current root directory for all operations.
+           **/
+          static const Pathname & defaultRoot() { return _defaultRoot; }
+      
+          /**
+           * @return Current directory (below root) that contains the rpmdb.
+           **/
+          static const Pathname & defaultDbPath() { return _defaultDbPath; }
+      
+          /**
+           * Adjust access to the given database location, making it the new
+           * default location on success. No relative Pathnames are allowed.
+           *
+           * It's not possible to access a database while access is blocked
+           * (see @ref blockAccess), but valid Pathnames provided will be stored
+           * as new default location.
+           *
+           * It's not allowed to switch to another location while a database
+           * is accessed. Use @ref dbRelease to force releasing the database first.
+          *
+           * \throws Exception
+           *
+           **/
+          static void dbAccess( const Pathname & root_r, const Pathname & dbPath_r );
+      
+          /**
+           * Same as &ref dbAccess(), but returns the database handle if
+           * avaialble, otherwise NULL. This creates an external reference, thus
+           * it should not be used longer than necessary. Be prepared that the
+           * handle might become invalid (see @ref dbRelease) later.
+           *
+           * \throws Exception
+           *
+           **/
+          static void dbAccess( librpmDb::constPtr & ptr_r );
+      
+          /**
+           * If there are no outstanding references to the database (e.g. by @ref db_const_iterator),
+           * the database is closed. Subsequent calls to @ref dbAccess may however
+           * open the database again.
+           *
+           * If forced, the internal reference is dropped and it will look like
+           * the database was closed. But physically the database will be closed
+           * after all outstanding references are gone.
+           *
+           * @return The number of outstandig references to the database, 0 if
+           * if database was physically closed.
+           **/
+          static unsigned dbRelease( bool force_r = false );
+      
+          /**
+           * Blocks further access to rpmdb. Basically the same as @ref dbRelease( true ),
+           * but subsequent calls to @ref dbAccess will fail returning E_RpmDB_access_blocked.
+           *
+           * @return The number of outstandig references to the database, 0 if
+           * if database was physically closed.
+           **/
+          static unsigned blockAccess();
+      
+          /**
+           * Allow access to rpmdb e.g. after @ref blockAccess. Subsequent calls to
+           * @ref dbAccess will perform.
+           *
+           * <B>NOTE:</B> Initially we're in blocked mode. So you must call @ref unblockAccess
+           * unblockAccess at least once. Othwise nothing will happen.
+           *
+           * @return The number of outstandig references to the database, 0 if
+           * if database was physically closed.
+           **/
+          static void unblockAccess();
+      
+          /**
+           * @return Whether database access is blocked.
+           **/
+          static bool isBlocked() { return _dbBlocked; }
+      
+          /**
+           * Dump debug info.
+           **/
+          static std::ostream & dumpState( std::ostream & str );
+      
+        public:
+      
+          /**
+           * Collect info about what kind of rpmdb seems to be present by
+           * looking at paths and filenames.
+           **/
+          class DbDirInfo;
+      
+          /**
+           * Subclass to retrieve database content.
+           **/
+          class db_const_iterator;
+      
+        private:
+          ///////////////////////////////////////////////////////////////////
+          //
+          //   internal database handle interface (nonstatic)
+          //
+          ///////////////////////////////////////////////////////////////////
+      
+          /**
+           * Hides librpm specific data
+           **/
+          class D;
+          D & _d;
+      
+        protected:
+      
+          /**
+           * Private constructor! librpmDb objects are to be created via
+           * static interface only.
+           **/
+          librpmDb( const Pathname & root_r, const Pathname & dbPath_r, bool readonly_r );
+      
+          /**
+           * Trigger from @ref Rep, after refCount was decreased.
+           **/
+          virtual void unref_to( unsigned refCount_r ) const;
+      
+        public:
+      
+          /**
+           * Destructor. Closes rpmdb.
+           **/
+          virtual ~librpmDb();
+      
+          /**
+           * @return This handles root directory for all operations.
+           **/
+          const Pathname & root() const;
+      
+          /**
+           * @return This handles directory that contains the rpmdb.
+           **/
+          const Pathname & dbPath() const;
+#warning uncomment
+#if 0
+      
+          /**
+           * Return any database error. Usg. if the database was
+           * blocked by calling @ref dbRelease(true) or @ref blockAccess.
+           **/
+          PMError error() const;
+      
+          /**
+           * @return Whether
+           **/
+          bool valid() const { return( ! error() ); }
+#endif
+      
+          /**
+           * @return True if handle is valid and database is empty.
+           **/
+          bool empty() const;
+      
+        public:
+      
+          /**
+           * Dont call it ;) It's for development and testing only.
+           **/
+          void * dont_call_it() const;
+      
+          /**
+           * Dump debug info.
+           **/
+          virtual std::ostream & dumpOn( std::ostream & str ) const;
+      };
+      
+      ///////////////////////////////////////////////////////////////////
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       CLASS NAME : librpmDb::DbDirInfo
+      /**
+       * Collect info about what kind of rpmdb seems to be present by
+       * looking at paths and filenames.
+       **/
+      class librpmDb::DbDirInfo {
+        friend std::ostream & operator<<( std::ostream & str, const DbDirInfo & obj );
+      
+        private:
+      
+          /**
+           * Root directory for all operations.
+           **/
+          Pathname _root;
+      
+          /**
+           * Directory that contains the rpmdb.
+           **/
+          Pathname _dbPath;
+      
+          /**
+           * database directory (unset on illegal constructor arguments)
+           **/
+          PathInfo _dbDir;
+      
+          /**
+           * rpmV4 database (_dbDir/Packages)
+           **/
+          PathInfo _dbV4;
+      
+          /**
+           * rpmV3 database (_dbDir/packages.rpm)
+           **/
+          PathInfo _dbV3;
+      
+          /**
+           * rpmV3 database backup created on conversion to rpmV4 (_dbDir/packages.rpm3)
+           **/
+          PathInfo _dbV3ToV4;
+      
+        public:
+      
+          /**
+           * For Constructor arguments see @ref accessPath. On illegal
+           * arguments @ref _dbDir is unset.
+           **/
+          DbDirInfo( const Pathname & root_r, const Pathname & dbPath_r );
+      
+        public:
+      
+          /**
+           * Root directory for all operations.
+           **/
+          const Pathname & root() const { return _root; }
+      
+          /**
+           * Directory that contains the rpmdb.
+           **/
+          const Pathname & dbPath() const { return _dbPath; }
+      
+          /**
+           * database directory (unset on illegal constructor arguments)
+           **/
+          const PathInfo & dbDir() const { return _dbDir; }
+      
+          /**
+           * rpmV4 database (_dbDir/Packages)
+           **/
+          const PathInfo & dbV4() const { return _dbV4; }
+      
+          /**
+           * rpmV3 database (_dbDir/packages.rpm)
+           **/
+          const PathInfo & dbV3() const { return _dbV3; }
+      
+          /**
+           * rpmV3 database backup created on conversion to rpmV4 (_dbDir/packages.rpm3)
+           **/
+          const PathInfo & dbV3ToV4() const { return _dbV3ToV4; }
+      
+        public:
+      
+          /**
+           * Restat all paths
+           **/
+          void restat();
+      
+        public:
+      
+          /**
+           * Whether constructor arguments were illegal.
+           **/
+          bool illegalArgs() const { return _dbDir.path().empty(); }
+      
+          /**
+           * Whether constructor arguments were llegal and dbDir either
+           * is a directory or may be created (path does not exist).
+           **/
+          bool usableArgs() const { return _dbDir.isDir() || ! ( _dbDir.path().empty() || _dbDir.isExist() ); }
+      
+          /**
+           * Whether dbDir directory exists.
+           **/
+          bool hasDbDir() const { return _dbDir.isDir(); }
+      
+          /**
+           * Whether dbV4 file exists.
+           **/
+          bool hasDbV4() const { return _dbV4.isFile(); }
+      
+          /**
+           * Whether dbV3 file exists.
+           **/
+          bool hasDbV3() const { return _dbV3.isFile(); }
+      
+          /**
+           * Whether dbV3ToV4 file exists.
+           **/
+          bool hasDbV3ToV4() const { return _dbV3ToV4.isFile(); }
+      };
+      
+      ///////////////////////////////////////////////////////////////////
+      
+      ///////////////////////////////////////////////////////////////////
+      //
+      //       CLASS NAME : librpmDb::db_const_iterator
+      /**
+       * @short Subclass to retrieve database content.
+       *
+       *
+       **/
+      class librpmDb::db_const_iterator {
+        db_const_iterator & operator=( const db_const_iterator & ); // NO ASSIGNMENT!
+        db_const_iterator ( const db_const_iterator & );            // NO COPY!
+        friend std::ostream & operator<<( std::ostream & str, const db_const_iterator & obj );
+        friend class librpmDb;
+      
+        private:
+      
+          /**
+           * Hides librpm specific data
+           **/
+          class D;
+          D & _d;
+      
+        public:
+      
+          /**
+           * Constructor. Iterator is initialized to @ref findAll.
+           * The default form accesses librpmDb's default database.
+           * Explicitly providing a database handle should not be
+           * neccesary, except for testing.
+           **/
+          db_const_iterator( librpmDb::constPtr dbptr_r = 0 );
+      
+          /**
+           * Destructor.
+           **/
+          ~db_const_iterator();
+      
+#warning FIXME this function
+#if 0
+          /**
+           * Return any database error.
+           *
+           * <B>NOTE:</B> If the database gets blocked (see @ref dbRelease)
+           * dbError will immediately report this, but an already running
+           * iteration will proceed to its end. Then the database is dropped.
+           **/
+          PMError dbError() const;
+#endif
+      
+          /**
+           * Advance to next RpmHeader::constPtr.
+           **/
+          void operator++();
+      
+          /**
+           * Returns the current headers index in database,
+           * 0 if no header.
+           **/
+          unsigned dbHdrNum() const;
+      
+          /**
+           * Returns the current RpmHeader::constPtr or
+           * NULL, if no more entries available.
+           **/
+          const RpmHeader::constPtr & operator*() const;
+      
+          /**
+           * Forwards to the current RpmHeader::constPtr.
+           **/
+          const RpmHeader::constPtr & operator->() const {
+            return operator*();
+          }
+      
+        public:
+      
+          /**
+           * Reset to iterate all packages. Returns true if iterator
+           * contains at least one entry.
+           *
+           * <B>NOTE:</B> No entry (false) migt be returned due to a
+           * meanwhile blocked database (see @ref dbRelease). Use
+           * @ref dbError to check this.
+           **/
+          bool findAll();
+      
+          /**
+           * Reset to iterate all packages that own a certain file.
+           **/
+          bool findByFile( const std::string & file_r );
+      
+          /**
+           * Reset to iterate all packages that provide a certain tag.
+           **/
+          bool findByProvides( const std::string & tag_r );
+      
+          /**
+           * Reset to iterate all packages that require a certain tag.
+           **/
+          bool findByRequiredBy( const std::string & tag_r );
+      
+          /**
+           * Reset to iterate all packages that conflict with a certain tag.
+           **/
+          bool findByConflicts( const std::string & tag_r );
+      
+          /**
+           * Reset to iterate all packages with a certain name.
+           *
+           * <B>NOTE:</B> Multiple entries for one package installed
+           * in different versions are possible but not desired. Usually
+           * you'll want to use @ref findPackage instead.
+           *
+           * findByName is needed to retrieve pseudo packages like
+           * 'gpg-pubkey', which in fact exist in multiple instances.
+           **/
+          bool findByName( const std::string & name_r );
+      
+        public:
+      
+          /**
+           * Find package by name.
+           *
+           * Multiple entries for one package installed in different versions
+           * are possible but not desired. If so, the last package installed
+           * is returned.
+           **/
+          bool findPackage( const std::string & name_r );
+      
+          /**
+           * Find package by name and edition.
+           * Commonly used by PMRpmPackageDataProvider.
+           **/
+          bool findPackage( const std::string & name_r, const Edition & ed_r );
+#warning define if eneded
+#if 0
+          /**
+           * Abbr. for <code>findPackage( which_r.name, which_r.edition );</code>
+           **/
+          bool findPackage( const PkgNameEd & which_r ) {
+            return findPackage( which_r.name, which_r.edition );
+          }
+#endif
+          /**
+           * Abbr. for <code>findPackage( which_r->name(), which_r->edition() );</code>
+           **/
+          bool findPackage( const Package::constPtr & which_r );
+      };
+
+///////////////////////////////////////////////////////////////////
+    } //namespace rpm
+  } //namespace target
+} // namespace zypp
+
+#endif // librpmDb_h
+