00001
00002
00003
00004
00005
00006
00007
00008
00012 #include <iostream>
00013 #include "zypp/base/Easy.h"
00014 #include "zypp/base/Logger.h"
00015 #include "zypp/base/LogTools.h"
00016 #include "zypp/base/Iterator.h"
00017 #include "zypp/base/String.h"
00018 #include "zypp/base/Gettext.h"
00019
00020 #include "zypp/parser/susetags/FileReaderBaseImpl.h"
00021 #include "zypp/parser/susetags/RepoParser.h"
00022 #include "zypp/parser/susetags/ContentFileReader.h"
00023 #include "zypp/parser/susetags/PackagesFileReader.h"
00024 #include "zypp/parser/susetags/PackagesLangFileReader.h"
00025 #include "zypp/parser/susetags/PackagesDuFileReader.h"
00026 #include "zypp/parser/susetags/PatternFileReader.h"
00027 #include "zypp/parser/susetags/RepoIndex.h"
00028 #include "zypp/parser/ParseException.h"
00029
00030 #include "zypp/ZConfig.h"
00031
00032 using std::endl;
00033 #undef ZYPP_BASE_LOGGER_LOGGROUP
00034 #define ZYPP_BASE_LOGGER_LOGGROUP "parser::susetags"
00035
00037 namespace zypp
00038 {
00039
00040 namespace parser
00041 {
00042
00043 namespace susetags
00044 {
00045
00047
00048
00049
00053 class RepoParser::Impl
00054 {
00055 public:
00056 Impl( const data::RecordId & repositoryId_r,
00057 data::ResolvableDataConsumer & consumer_r,
00058 const ProgressData::ReceiverFnc & fnc_r )
00059 : _repositoryId( repositoryId_r )
00060 , _consumer( consumer_r )
00061 {
00062 _ticks.sendTo( fnc_r );
00063 }
00064
00066 void parse( const Pathname & reporoot_r );
00067
00070
00071 void consumeIndex( const RepoIndex_Ptr & data_r )
00072 {
00073
00074 _repoIndex = data_r;
00075 }
00076
00078 void consumeProd( const data::Product_Ptr & data_r )
00079 {
00080 MIL << "[Product] " << data_r << endl;
00081 ++_stats.prod;
00082 _prodData = data_r;
00083 _defaultVendor = data_r->vendor;
00084 _consumer.consumeProduct( _repositoryId, data_r );
00085 }
00086
00088 void consumePkg( const data::Package_Ptr & data_r )
00089 {
00090 fixVendor( data_r );
00091 fixLocationPath( data_r );
00092 resolveSharedDataTag( data_r );
00093
00094 ++_stats.pack;
00095 data::RecordId newid = _consumer.consumePackage( _repositoryId, data_r );
00096
00097
00098 idMapAdd( makeSharedIdent( ResTraits<Package>::kind,
00099 data_r->name,
00100 data_r->edition,
00101 data_r->arch ),
00102 newid );
00103 }
00104
00106 void consumeSrcPkg( const data::SrcPackage_Ptr & data_r )
00107 {
00108 fixVendor( data_r );
00109 fixLocationPath( data_r );
00110 resolveSharedDataTag( data_r );
00111
00112 ++_stats.srcp;
00113 data::RecordId newid = _consumer.consumeSourcePackage( _repositoryId, data_r );
00114
00115
00116 idMapAdd( makeSharedIdent( ResTraits<SrcPackage>::kind,
00117 data_r->name,
00118 data_r->edition,
00119 data_r->arch ),
00120 newid );
00121 }
00122
00124 void consumePkgLang( const data::Package_Ptr & data_r )
00125 {
00126 data::RecordId id = idMapGet( makeSharedIdent( ResTraits<Package>::kind,
00127 data_r->name,
00128 data_r->edition,
00129 data_r->arch ) );
00130 if ( id != data::noRecordId )
00131 {
00132 _consumer.updatePackageLang( id, data_r );
00133 }
00134 }
00135
00137 void consumeSrcPkgLang( const data::SrcPackage_Ptr & data_r )
00138 {
00139 data::RecordId id = idMapGet( makeSharedIdent( ResTraits<SrcPackage>::kind,
00140 data_r->name,
00141 data_r->edition,
00142 data_r->arch ) );
00143 if ( id != data::noRecordId )
00144 {
00145 _consumer.updatePackageLang( id, data_r );
00146 }
00147 }
00148
00150 void consumePkgDu( const data::Package_Ptr & data_r )
00151 {
00152 data::RecordId id = idMapGet( makeSharedIdent( ResTraits<Package>::kind,
00153 data_r->name,
00154 data_r->edition,
00155 data_r->arch ) );
00156 if ( id != data::noRecordId )
00157 {
00158 _consumer.consumeDiskUsage( id, data_r->diskusage );
00159 }
00160 }
00161
00163 void consumeSrcPkgDu( const data::SrcPackage_Ptr & data_r )
00164 {
00165 data::RecordId id = idMapGet( makeSharedIdent( ResTraits<SrcPackage>::kind,
00166 data_r->name,
00167 data_r->edition,
00168 data_r->arch ) );
00169 if ( id != data::noRecordId )
00170 {
00171 _consumer.consumeDiskUsage( id, data_r->diskusage );
00172 }
00173 }
00174
00176 void consumePat( const data::Pattern_Ptr & data_r )
00177 {
00178
00179 fixVendor( data_r );
00180 ++_stats.patt;
00181 _consumer.consumePattern( _repositoryId, data_r );
00182 }
00184
00185 public:
00187 void fixVendor( const data::ResObject_Ptr & data_r )
00188 {
00189 if ( data_r->vendor.empty() && ! _defaultVendor.empty() )
00190 {
00191 data_r->vendor = _defaultVendor;
00192 }
00193 }
00194
00196 void fixLocationPath( const data::Packagebase_Ptr & data_r )
00197 {
00198 Pathname tofix( data_r->repositoryLocation.filename() );
00199 data_r->repositoryLocation.changeFilename( _datadir / tofix );
00200 }
00201
00203 void resolveSharedDataTag( const data::Packagebase_Ptr & data_r )
00204 {
00205 if ( ! data_r->sharedDataTag.empty() )
00206 {
00207 data_r->shareDataWith = idMapGet( data_r->sharedDataTag );
00208 }
00209 }
00210
00211 public:
00215 Pathname assertMandatoryFile( const Pathname & file_r, bool lookgz = true ) const
00216 {
00217 if ( lookgz )
00218 {
00219 PathInfo gzinputfile( _reporoot / (file_r.extend(".gz")) );
00220 if ( !gzinputfile.isFile() )
00221 {
00222 WAR << _reporoot << ": Skip gz required file (will look for non-gz): " << file_r.extend(".gz").asString() << endl;
00223 }
00224 else
00225 {
00226 return gzinputfile.path();
00227 }
00228 }
00229 PathInfo inputfile( _reporoot / file_r );
00230 if ( ! inputfile.isFile() )
00231 {
00232 ZYPP_THROW( ParseException( _reporoot.asString() + ": " + _("Required file is missing: ") + file_r.asString() ) );
00233 }
00234 return inputfile.path();
00235 }
00236
00240 Pathname getOptionalFile( const Pathname & file_r, bool lookgz = true ) const
00241 {
00242 if ( lookgz )
00243 {
00244 PathInfo gzinputfile( _reporoot / (file_r.extend(".gz")) );
00245 if ( !gzinputfile.isFile() )
00246 {
00247
00248 }
00249 else
00250 {
00251 return gzinputfile.path();
00252 }
00253 }
00254 PathInfo inputfile( _reporoot / file_r );
00255 if ( ! inputfile.isFile() )
00256 {
00257
00258 return Pathname();
00259 }
00260 return inputfile.path();
00261 }
00262
00263 bool isPatternFile( const std::string & name_r ) const
00264 {
00265 return(
00266 (name_r.size() > 4 && name_r.substr( name_r.size() - 4 ) == ".pat" )
00267 || (name_r.size() > 7 && name_r.substr( name_r.size() - 7 ) == ".pat.gz" )
00268 );
00269 }
00270
00272 bool haveLocale( const Locale & locale_r ) const
00273 {
00274 std::string searchFor( "packages." + locale_r.code() );
00275 for ( RepoIndex::FileChecksumMap::const_iterator it = _repoIndex->metaFileChecksums.begin();
00276 it != _repoIndex->metaFileChecksums.end(); ++it )
00277 {
00278 if ( it->first == searchFor )
00279 {
00280
00281 PathInfo inputfile( _reporoot / _descrdir / searchFor );
00282 if ( ! inputfile.isFile() )
00283 {
00284 WAR << "Known and desired file is not on disk: " << inputfile << endl;
00285 }
00286 else
00287 return true;
00288 }
00289
00290 if ( it->first == (searchFor+".gz") )
00291 {
00292
00293 PathInfo inputfile( _reporoot / _descrdir / (searchFor+".gz") );
00294 if ( ! inputfile.isFile() )
00295 {
00296 WAR << "Known and desired file is not on disk: " << inputfile << endl;
00297 }
00298 else
00299 return true;
00300 }
00301 }
00302 return false;
00303 }
00304
00308 void parseLocaleIf( const Locale & locale_r )
00309 {
00310 Locale toParse( locale_r );
00311 bool alreadyParsed = false;
00312
00313 while ( toParse != Locale::noCode )
00314 {
00315 alreadyParsed = ( _parsedLocales.find( toParse ) != _parsedLocales.end() );
00316 if ( alreadyParsed || haveLocale( toParse ) )
00317 break;
00318 toParse = toParse.fallback();
00319 }
00320
00321 if ( toParse != locale_r )
00322 {
00323 WAR << "Using fallback locale " << toParse << " for " << locale_r << endl;
00324 }
00325
00326 if ( alreadyParsed )
00327 {
00328 return;
00329 }
00330
00331 _parsedLocales.insert( toParse );
00332
00333 Pathname inputfile( getOptionalFile( _descrdir / ("packages." + toParse.code()) ) );
00334 if ( ! inputfile.empty() )
00335 {
00336 PackagesLangFileReader reader;
00337 reader.setLocale( toParse );
00338 reader.setPkgConsumer( bind( &Impl::consumePkgLang, this, _1 ) );
00339 reader.setSrcPkgConsumer( bind( &Impl::consumeSrcPkgLang, this, _1 ) );
00340 CombinedProgressData progress( _ticks, PathInfo(inputfile).size() );
00341 reader.parse( inputfile, progress );
00342 }
00343
00344 if ( ! _ticks.incr( PathInfo(inputfile).size() ) )
00345 ZYPP_THROW( AbortRequestException() );
00346 }
00347
00348 private:
00349 void idMapAdd( const std::string & key_r, data::RecordId value_r )
00350 {
00351 if ( _idMap[key_r] != data::noRecordId )
00352 {
00353 WAR << "Multiple record ids for " << key_r
00354 << " (first " << _idMap[key_r] << ", now " << value_r << ")" << endl;
00355 }
00356 _idMap[key_r] = value_r;
00357 }
00358
00359 data::RecordId idMapGet( const std::string & key_r )
00360 {
00361 data::RecordId ret = _idMap[key_r];
00362 if ( ret == data::noRecordId )
00363 {
00364
00365 }
00366 return ret;
00367 }
00368
00369 private:
00370 data::RecordId _repositoryId;
00371 data::ResolvableDataConsumer & _consumer;
00372 ProgressData _ticks;
00373
00374 private:
00375 RepoIndex_Ptr _repoIndex;
00376 data::Product_Ptr _prodData;
00377 std::string _defaultVendor;
00378 Pathname _reporoot;
00379 Pathname _descrdir;
00380 Pathname _datadir;
00381
00383 std::set<Locale> _parsedLocales;
00384
00386 std::map<std::string,data::RecordId> _idMap;
00387
00388 struct Stats {
00389 DefaultIntegral<unsigned,0> prod;
00390 DefaultIntegral<unsigned,0> patt;
00391 DefaultIntegral<unsigned,0> pack;
00392 DefaultIntegral<unsigned,0> srcp;
00393 };
00394 Stats _stats;
00395 };
00397
00399
00400
00401
00402
00403 void RepoParser::Impl::parse( const Pathname & reporoot_r )
00404 {
00405 _prodData = 0;
00406 _repoIndex = 0;
00407 _defaultVendor.clear();
00408 _reporoot = reporoot_r;
00409 _descrdir = _datadir = Pathname();
00410 _parsedLocales.clear();
00411
00412
00413 {
00414 Pathname inputfile( assertMandatoryFile( "content" ) );
00415 ContentFileReader content;
00416 content.setProductConsumer( bind( &Impl::consumeProd, this, _1 ) );
00417 content.setRepoIndexConsumer( bind( &Impl::consumeIndex, this, _1 ) );
00418 content.parse( inputfile );
00419 }
00420 if ( ! _repoIndex )
00421 {
00422 ZYPP_THROW( ParseException( reporoot_r.asString() + ": " + "No repository index in content file." ) );
00423 }
00424 DBG << _repoIndex << endl;
00425
00426
00427 _descrdir = _repoIndex->descrdir;
00428 _datadir = _repoIndex->datadir;
00429
00430 _ticks.name( "Parsing susetags repo at " + reporoot_r.asString() );
00431
00432
00433 int jobssize = 0;
00434 for ( RepoIndex::FileChecksumMap::const_iterator it = _repoIndex->metaFileChecksums.begin();
00435 it != _repoIndex->metaFileChecksums.end(); ++it )
00436 {
00437
00438 jobssize += PathInfo(getOptionalFile(_descrdir / it->first, false)).size();
00439
00440 }
00441 MIL << "Total job size: " << jobssize << endl;
00442
00443 _ticks.range(jobssize);
00444
00445 if ( ! _ticks.toMin() )
00446 ZYPP_THROW( AbortRequestException() );
00447
00448
00449 {
00450 Pathname inputfile( assertMandatoryFile( _descrdir / "packages" ) );
00451 PackagesFileReader reader;
00452 reader.setPkgConsumer( bind( &Impl::consumePkg, this, _1 ) );
00453 reader.setSrcPkgConsumer( bind( &Impl::consumeSrcPkg, this, _1 ) );
00454
00455 CombinedProgressData progress( _ticks, PathInfo(inputfile).size() );
00456 reader.parse( inputfile, progress );
00457 }
00458
00459
00460
00461
00462 assertMandatoryFile( _descrdir / "packages.en" );
00463 parseLocaleIf( Locale("en") );
00464
00465
00466 parseLocaleIf( ZConfig::instance().textLocale() );
00467
00468
00469
00470 {
00471 Pathname inputfile( getOptionalFile( _descrdir / "packages.DU" ) );
00472 if ( ! inputfile.empty() )
00473 {
00474 PackagesDuFileReader reader;
00475 reader.setPkgConsumer( bind( &Impl::consumePkgDu, this, _1 ) );
00476 reader.setSrcPkgConsumer( bind( &Impl::consumeSrcPkgDu, this, _1 ) );
00477
00478 CombinedProgressData progress( _ticks, PathInfo(inputfile).size() );
00479 reader.parse( inputfile, progress );
00480 }
00481 }
00482
00483
00484 for ( RepoIndex::FileChecksumMap::const_iterator it = _repoIndex->metaFileChecksums.begin();
00485 it != _repoIndex->metaFileChecksums.end(); ++it )
00486 {
00487 if ( isPatternFile( it->first ) )
00488 {
00489
00490
00491
00492
00493
00494
00495
00496 std::vector<std::string> patparts;
00497
00498
00499 unsigned archoff = 2;
00500
00501 unsigned count = str::split( it->first, std::back_inserter( patparts ), "." );
00502
00503 if ( patparts[count-1] == "gz" )
00504 archoff++;
00505
00506 if ( count > archoff )
00507 {
00508 try
00509 {
00510 Arch patarch( patparts[count-archoff] );
00511 if ( !patarch.compatibleWith( ZConfig::instance().systemArchitecture() ) )
00512 {
00513
00514 MIL << "Discarding pattern " << it->first << endl;
00515 continue;
00516 }
00517 }
00518 catch ( const Exception & excpt )
00519 {
00520 WAR << "Pattern file name does not contain recognizable architecture: " << it->first << endl;
00521
00522 }
00523 }
00524
00525 Pathname inputfile( getOptionalFile( _descrdir / it->first, false ) );
00526 if ( ! inputfile.empty() )
00527 {
00528 PatternFileReader reader;
00529 reader.setConsumer( bind( &Impl::consumePat, this, _1 ) );
00530 CombinedProgressData progress( _ticks, PathInfo(inputfile).size() );
00531 reader.parse( inputfile, progress );
00532 }
00533 }
00534 }
00535
00536
00537 if ( ! _ticks.toMax() )
00538 ZYPP_THROW( AbortRequestException() );
00539
00540 MIL << "DONE " << reporoot_r << "("
00541 << _stats.prod << " products, "
00542 << _stats.patt << " patterns, "
00543 << _stats.pack << " packages, "
00544 << _stats.srcp << " srcpackages)" << endl;
00545 }
00547
00548
00549
00551
00553
00554
00555
00556
00557 RepoParser::RepoParser( const data::RecordId & repositoryId_r,
00558 data::ResolvableDataConsumer & consumer_r,
00559 const ProgressData::ReceiverFnc & fnc_r )
00560 : _pimpl( new Impl( repositoryId_r, consumer_r, fnc_r ) )
00561 {}
00562
00564
00565
00566
00567
00568 RepoParser::~RepoParser()
00569 {}
00570
00572
00573
00574
00575
00576 void RepoParser::parse( const Pathname & reporoot_r )
00577 {
00578 _pimpl->parse( reporoot_r );
00579 }
00580
00582 }
00585 }
00588 }