libept
record.h
Go to the documentation of this file.
00001 // -*- C++ -*-
00002 
00003 #include <ept/core/apt/recordparser.h>
00004 
00005 #ifndef EPT_APT_RECORD_H
00006 #define EPT_APT_RECORD_H
00007 
00008 namespace ept {
00009 namespace core {
00010 namespace record {
00011 
00012 struct Source;
00013 
00014 struct InternalList {
00015     Source *m_source;
00016     size_t m_idx;
00017 
00018     Internal head();
00019     const Internal head() const;
00020     bool empty() const;
00021 
00022     InternalList tail() const {
00023         InternalList t = *this;
00024         ++ t.m_idx;
00025         return t;
00026     }
00027 
00028     InternalList( Source &s )
00029         : m_source( &s ), m_idx( 0 )
00030     {}
00031 };
00032 
00033 struct Setup {
00034     typedef ept::Token Token;
00035     typedef record::Internal Internal;
00036     typedef record::PropertyId PropertyId;
00037     typedef record::InternalList InternalList;
00038 };
00039 
00040 template<> struct PropertyType< InstalledSize > { typedef int T; };
00041 template<> struct PropertyType< PackageSize > { typedef int T; };
00042 
00043 struct Parser: RecordParser
00044 {
00045     bool parseBool(bool& def, const std::string& str) const
00046     {
00047         // Believe it or not, this is what apt does to interpret bool fields
00048         if (str == "no" || str == "false" || str == "without" ||
00049             str == "off" || str == "disable")
00050             return false;
00051 
00052         if (str == "yes" || str == "true" || str == "with" ||
00053             str == "on" || str == "enable")
00054             return true;
00055 
00056         return def;
00057     }
00058 
00059 public:
00060     Parser() : RecordParser() {}
00061     Parser(const std::string& str) : RecordParser(str) {}
00062 
00063     template< PropertyId p >
00064     typename PropertyType< p >::T parse( typename PropertyType< p >::T def,
00065                                          std::string data );
00066 
00067     template< typename T >
00068     struct Default {
00069         static T def;
00070     };
00071 
00072     template< typename T > T parse( const T &def,
00073                                     const std::string &field ) const;
00074 
00075     template< PropertyId p >
00076     typename PropertyType< p >::T get(
00077         const typename PropertyType< p >::T &def
00078         = Default< typename PropertyType< p >::T >::def ) const
00079     {
00080         return parse< typename PropertyType< p >::T >( def,
00081                                                        lookup( fields[ p ] ) );
00082     }
00083 
00084 };
00085 
00086 template< typename T > T Parser::Default< T >::def = T();
00087 
00088 template<> inline std::string Parser::get< ShortDescription >(
00089     const std::string& def ) const
00090 {
00091     std::string str = lookup( fields[ Description ] );
00092     if (str == std::string())
00093         return def;
00094     size_t pos = str.find("\n");
00095     if (pos == std::string::npos)
00096         return str;
00097     else
00098         return str.substr(0, pos);
00099 }
00100 
00101 template<> inline std::string Parser::get< LongDescription >(
00102     const std::string& def ) const
00103 {
00104     std::string str = lookup( fields[ Description ] );
00105     if (str == std::string())
00106         return def;
00107     size_t pos = str.find("\n");
00108     if (pos == std::string::npos)
00109         return str;
00110     else
00111     {
00112         // Trim trailing spaces
00113         for (++pos; pos < str.size() && isspace(str[pos]); ++pos)
00114             ;
00115         return str.substr(pos);
00116     }
00117 }
00118 
00119 template<> inline std::string Parser::parse< std::string >(
00120     const std::string& def, const std::string& str) const
00121 {
00122     if (str == std::string())
00123         return def;
00124     return str;
00125 }
00126 
00127 template<> inline int Parser::parse< int >(
00128     const int& def, const std::string& str) const
00129 {
00130     if (str == string())
00131         return def;
00132     return (size_t)strtoul(str.c_str(), NULL, 10);
00133 }
00134 
00135 struct Source : core::Source< Source, Setup, PropertyType >
00136 {
00137     AptDatabase &m_db;
00138 
00139     /* caching */
00140     pkgCache::PkgFileIterator lastFile;
00141     FileFd file;
00142     size_t lastOffset;
00143 
00144     /* in-order retrieval of records, for InternalList */
00145     typedef vector< pkgCache::VerFile * > VfList;
00146     VfList m_vflist;
00147 
00148     VfList &vfList() {
00149         if ( m_vflist.size() > 0 )
00150             return m_vflist;
00151 
00152         m_vflist.reserve(m_db.cache().HeaderP->PackageCount + 1);
00153 
00154         // Populate the vector of versions to print
00155         for (pkgCache::PkgIterator pi = m_db.cache().PkgBegin(); !pi.end(); ++pi)
00156         {
00157             if (pi->VersionList == 0)
00158                 continue;
00159 
00160             for( pkgCache::VerIterator vi = pi.VersionList(); !vi.end(); ++vi ) {
00161 
00162                 // Choose a valid file that contains the record for this version
00163                 pkgCache::VerFileIterator vfi = vi.FileList();
00164                 for ( ; !vfi.end(); ++vfi )
00165                     if ((vfi.File()->Flags & pkgCache::Flag::NotSource) == 0)
00166                         break;
00167 
00168                 if ( !vfi.end() )
00169                     m_vflist.push_back( vfi );
00170             }
00171         }
00172 
00173         sort(m_vflist.begin(), m_vflist.end(), localityCompare);
00174         return m_vflist;
00175     }
00176 
00177     Source( AptDatabase &db ) : m_db( db ) {}
00178 
00179     InternalList listInternal() {
00180         return InternalList( *this );
00181     }
00182 
00183     Internal lookupToken( Token t ) {
00184         return m_db.lookupVersionFile( m_db.lookupVersion( t ) );
00185     }
00186 
00187     // Sort a version list by package file locality
00188     static bool localityCompare(const pkgCache::VerFile* a,
00189                                 const pkgCache::VerFile* b)
00190     {
00191         if (a == 0 && b == 0)
00192             return false;
00193         if (a == 0)
00194             return true;
00195         if (b == 0)
00196             return false;
00197 
00198         if (a->File == b->File)
00199             return a->Offset < b->Offset;
00200         return a->File < b->File;
00201     }
00202 
00203     void invalidate() {
00204         core::Source< Source, Setup, PropertyType >::invalidate();
00205         lastFile = pkgCache::PkgFileIterator();
00206     }
00207 
00208     std::string getRecord( Internal vfi ) {
00209         if ( vfi.Cache() == 0 || vfi.end() )
00210             return "";
00211 
00212         if ((lastFile.Cache() == 0)
00213             || vfi->File + m_db.cache().PkgFileP != lastFile)
00214         {
00215             lastFile = pkgCache::PkgFileIterator(
00216                 m_db.cache(), vfi->File + m_db.cache().PkgFileP);
00217             if (!lastFile.IsOk())
00218                 throw wibble::exception::System(
00219                     std::string("Reading the"
00220                                 " data record for a package from file ")
00221                     + lastFile.FileName() );
00222             if (file.IsOpen())
00223                 file.Close();
00224             if (!file.Open(lastFile.FileName(), FileFd::ReadOnly))
00225                 throw wibble::exception::System( std::string("Opening file ")
00226                                                  + lastFile.FileName() );
00227             lastOffset = 0;
00228         }
00229 
00230         // If we start near were we ended, avoid a seek
00231         // and enlarge the read a bit
00232         size_t slack = vfi->Offset - lastOffset;
00233         if ( slack > 128 ) // mornfall: was 8, making it 128
00234         {
00235             slack = 0;
00236             if ( !file.Seek( vfi->Offset ) )
00237                 throw wibble::exception::System(
00238                     std::string("Cannot seek to package record in file ")
00239                     + lastFile.FileName() );
00240         }
00241 
00242         char buffer[vfi->Size + slack + 1];
00243         if (!file.Read(buffer, vfi->Size + slack))
00244             throw wibble::exception::System(
00245                 std::string("Cannot read package "
00246                             "record in file ") + lastFile.FileName() );
00247 
00248         buffer[vfi->Size + slack] = '\0';
00249         //cerr << "Data read (slack: " << slack << ")" << endl;
00250 
00251         lastOffset = vfi->Offset + vfi->Size;
00252 
00253         return string(buffer+slack);
00254     }
00255 
00256     Token getToken( Internal i ) {
00257         Token t;
00258         t._id = getInternal< Name >( i ) + "_" + getInternal< Version >( i );
00259         return t;
00260     }
00261 
00262     template< PropertyId p >
00263     typename PropertyType< p >::T getInternal( Internal i ) {
00264         Parser rec( getRecord( i ) );
00265         return rec.get< p >();
00266     }
00267 };
00268 
00269 template<> inline std::string Source::getInternal< Record >( Internal i ) {
00270     assert( !i.end() );
00271     return getRecord( i );
00272 }
00273 
00274 inline const Internal InternalList::head() const {
00275     return pkgCache::VerFileIterator( m_source->m_db.cache(),
00276                                       m_source->vfList()[ m_idx ] );
00277 }
00278 
00279 inline bool InternalList::empty() const {
00280     return m_idx == m_source->vfList().size();
00281 }
00282 
00283 
00284 }
00285 }
00286 }
00287 
00288 #endif