00001
00002
00003
00004
00005
00006
00007
00008
00012 #include <iostream>
00013 #include <sstream>
00014
00015 #include "zypp/base/LogTools.h"
00016 #include "zypp/base/String.h"
00017 #include "zypp/base/IOStream.h"
00018 #include "zypp/base/UserRequestException.h"
00019 #include "zypp/parser/ParseException.h"
00020
00021 #include "zypp/parser/susetags/ContentFileReader.h"
00022 #include "zypp/parser/susetags/RepoIndex.h"
00023 #include "zypp/data/ResolvableData.h"
00024 #include "zypp/CapFactory.h"
00025
00026 #include "zypp/ZConfig.h"
00027
00028 using std::endl;
00029 #undef ZYPP_BASE_LOGGER_LOGGROUP
00030 #define ZYPP_BASE_LOGGER_LOGGROUP "parser::susetags"
00031
00033 namespace zypp
00034 {
00035
00036 namespace parser
00037 {
00038
00039 namespace susetags
00040 {
00041
00043
00044
00045
00047 struct ContentFileReader::Impl
00048 {
00049 public:
00050 Impl( const ContentFileReader & parent_r )
00051 : _parent( parent_r )
00052 {}
00053
00054 data::Product & product()
00055 {
00056 if ( !_product )
00057 _product = new data::Product;
00058 return *_product;
00059 }
00060
00061 RepoIndex & repoindex()
00062 {
00063 if ( !_repoindex )
00064 _repoindex = new RepoIndex;
00065 return *_repoindex;
00066 }
00067
00068 bool hasProduct() const
00069 { return _product; }
00070
00071 bool hasRepoIndex() const
00072 { return _repoindex; }
00073
00074 data::Product_Ptr handoutProduct()
00075 {
00076 data::Product_Ptr ret;
00077 ret.swap( _product );
00078 _product = 0;
00079 return ret;
00080 }
00081
00082 RepoIndex_Ptr handoutRepoIndex()
00083 {
00084 RepoIndex_Ptr ret;
00085 ret.swap( _repoindex );
00086 _repoindex = 0;
00087 return ret;
00088 }
00089
00090 public:
00091 bool isRel( const std::string & rel_r ) const
00092 {
00093 try
00094 {
00095 Rel( rel_r );
00096 return true;
00097 }
00098 catch (...)
00099 {}
00100 return false;
00101 }
00102
00103 bool setUrlList( std::list<Url> & list_r, const std::string & value ) const
00104 {
00105 bool errors = false;
00106 std::list<std::string> urls;
00107 if ( str::split( value, std::back_inserter(urls) ) )
00108 {
00109 for ( std::list<std::string>::const_iterator it = urls.begin();
00110 it != urls.end(); ++it )
00111 {
00112 try
00113 {
00114 list_r.push_back( *it );
00115 }
00116 catch( const Exception & excpt_r )
00117 {
00118 WAR << *it << ": " << excpt_r << endl;
00119 errors = true;
00120 }
00121 }
00122 }
00123 return errors;
00124 }
00125
00126 void setDependencies( data::DependencyList & deplist_r, const std::string & value ) const
00127 {
00128 std::list<std::string> words;
00129 str::split( value, std::back_inserter( words ) );
00130
00131 for ( std::list<std::string>::const_iterator it = words.begin();
00132 it != words.end(); ++it )
00133 {
00134 Resolvable::Kind kind( ResTraits<Package>::kind );
00135
00136 std::string name = *it;
00137 std::string::size_type colon = name.find( ":" );
00138 if ( colon != std::string::npos )
00139 {
00140 std::string skind( name, 0, colon );
00141 name.erase( 0, colon+1 );
00142
00143 if ( skind == ResTraits<Pattern>::kind )
00144 kind = ResTraits<Pattern>::kind;
00145 else if ( skind == ResTraits<Patch>::kind )
00146 kind = ResTraits<Patch>::kind;
00147 else if ( skind == ResTraits<Product>::kind )
00148 kind = ResTraits<Product>::kind;
00149 else if ( skind == ResTraits<Selection>::kind )
00150 kind = ResTraits<Selection>::kind;
00151 else if ( skind != ResTraits<Package>::kind )
00152 {
00153
00154 name = skind + ":" + name;
00155 }
00156 }
00157
00158
00159 std::list<std::string>::const_iterator next = it;
00160 if ( ++next != words.end()
00161 && (*next).find_first_of( "<>=" ) != std::string::npos )
00162 {
00163 std::string op = *next;
00164 if ( ++next != words.end() )
00165 {
00166 name += " ";
00167 name += op;
00168 name += " ";
00169 name += *next;
00170 it = next;
00171 }
00172 }
00173
00174
00175 deplist_r.insert( capability::parse( kind, name ) );
00176 }
00177 }
00178
00179 bool setFileCheckSum( std::map<std::string, CheckSum> & map_r, const std::string & value ) const
00180 {
00181 bool error = false;
00182 std::vector<std::string> words;
00183 if ( str::split( value, std::back_inserter( words ) ) == 3 )
00184 {
00185 map_r[words[2]] = CheckSum( words[0], words[1] );
00186 }
00187 else
00188 {
00189 error = true;
00190 }
00191 return error;
00192 }
00193
00194 public:
00195 std::string _inputname;
00196
00197 private:
00198 const ContentFileReader & _parent;
00199 data::Product_Ptr _product;
00200 RepoIndex_Ptr _repoindex;
00201 };
00203
00205
00206
00207
00209
00211
00212
00213
00214
00215 ContentFileReader::ContentFileReader()
00216 {}
00217
00219
00220
00221
00222
00223 ContentFileReader::~ContentFileReader()
00224 {}
00225
00227
00228
00229
00230
00231 void ContentFileReader::beginParse()
00232 {
00233 _pimpl.reset( new Impl(*this) );
00234 }
00235
00237
00238
00239
00240
00241 void ContentFileReader::endParse()
00242 {
00243
00244 if ( _pimpl->hasProduct() )
00245 {
00246 if ( _productConsumer )
00247 _productConsumer( _pimpl->handoutProduct() );
00248 }
00249 if ( _pimpl->hasRepoIndex() )
00250 {
00251 if ( _repoIndexConsumer )
00252 _repoIndexConsumer( _pimpl->handoutRepoIndex() );
00253 }
00254
00255 MIL << "[Content]" << endl;
00256 _pimpl.reset();
00257 }
00258
00260
00261
00262
00263
00264 void ContentFileReader::userRequestedAbort( unsigned lineNo_r )
00265 {
00266 ZYPP_THROW( AbortRequestException( errPrefix( lineNo_r ) ) );
00267 }
00268
00270
00271
00272
00273
00274 std::string ContentFileReader::errPrefix( unsigned lineNo_r,
00275 const std::string & msg_r,
00276 const std::string & line_r ) const
00277 {
00278 return str::form( "%s:%u:%s | %s",
00279 _pimpl->_inputname.c_str(),
00280 lineNo_r,
00281 line_r.c_str(),
00282 msg_r.c_str() );
00283 }
00284
00286
00287
00288
00289
00290 void ContentFileReader::parse( const InputStream & input_r,
00291 const ProgressData::ReceiverFnc & fnc_r )
00292 {
00293 MIL << "Start parsing " << input_r << endl;
00294 if ( ! input_r.stream() )
00295 {
00296 std::ostringstream s;
00297 s << "Can't read bad stream: " << input_r;
00298 ZYPP_THROW( ParseException( s.str() ) );
00299 }
00300 beginParse();
00301 _pimpl->_inputname = input_r.name();
00302
00303 ProgressData ticks( makeProgressData( input_r ) );
00304 ticks.sendTo( fnc_r );
00305 if ( ! ticks.toMin() )
00306 userRequestedAbort( 0 );
00307
00308 Arch sysarch( ZConfig::instance().systemArchitecture() );
00309
00310 iostr::EachLine line( input_r );
00311 for( ; line; line.next() )
00312 {
00313
00314 std::string value( *line );
00315 std::string key( str::stripFirstWord( value, true ) );
00316
00317 if ( key.empty() || *key.c_str() == '#' )
00318 {
00319 continue;
00320 }
00321
00322
00323 std::string modifier;
00324 std::string::size_type pos = key.rfind( '.' );
00325 if ( pos != std::string::npos )
00326 {
00327 modifier = key.substr( pos+1 );
00328 key.erase( pos );
00329 }
00330
00331
00332
00333
00334 if ( key == "PRODUCT" )
00335 {
00336 std::replace( value.begin(), value.end(), ' ', '_' );
00337 _pimpl->product().name = value;
00338 }
00339 else if ( key == "VERSION" )
00340 {
00341 _pimpl->product().edition = value;
00342 }
00343 else if ( key == "ARCH" )
00344 {
00345
00346
00347
00348 Arch carch( modifier );
00349 if ( Arch::compare( _pimpl->product().arch, carch ) < 0
00350 && carch.compatibleWith( sysarch ) )
00351 {
00352 _pimpl->product().arch = carch;
00353 }
00354 }
00355 else if ( key == "DISTPRODUCT" )
00356 {
00357 _pimpl->product().distributionName = value;
00358 }
00359 else if ( key == "DISTVERSION" )
00360 {
00361 _pimpl->product().distributionEdition = value;
00362 }
00363 else if ( key == "VENDOR" )
00364 {
00365 _pimpl->product().vendor = value;
00366 }
00367 else if ( key == "LABEL" )
00368 {
00369 _pimpl->product().summary.setText( value, Locale(modifier) );
00370 }
00371 else if ( key == "SHORTLABEL" )
00372 {
00373 _pimpl->product().shortName.setText( value, Locale(modifier) );
00374 }
00375 else if ( key == "TYPE" )
00376 {
00377 _pimpl->product().type = value;
00378 }
00379 else if ( key == "RELNOTESURL" )
00380 {
00381 for( std::string::size_type pos = value.find("%a");
00382 pos != std::string::npos;
00383 pos = value.find("%a") )
00384 {
00385 value.replace( pos, 2, sysarch.asString() );
00386 }
00387 try
00388 {
00389 _pimpl->product().releasenotesUrl = value;
00390 }
00391 catch( const Exception & excpt_r )
00392 {
00393 WAR << errPrefix( line.lineNo(), excpt_r.asString(), *line ) << endl;
00394 }
00395 }
00396 else if ( key == "UPDATEURLS" )
00397 {
00398 if ( _pimpl->setUrlList( _pimpl->product().updateUrls, value ) )
00399 {
00400 WAR << errPrefix( line.lineNo(), "Ignored malformed URL(s)", *line ) << endl;
00401 }
00402 }
00403 else if ( key == "EXTRAURLS" )
00404 {
00405 if ( _pimpl->setUrlList( _pimpl->product().extraUrls, value ) )
00406 {
00407 WAR << errPrefix( line.lineNo(), "Ignored malformed URL(s)", *line ) << endl;
00408 }
00409 }
00410 else if ( key == "OPTIONALURLS" )
00411 {
00412 if ( _pimpl->setUrlList( _pimpl->product().optionalUrls, value ) )
00413 {
00414 WAR << errPrefix( line.lineNo(), "Ignored malformed URL(s)", *line ) << endl;
00415 }
00416 }
00417 else if ( key == "PREREQUIRES" )
00418 {
00419 _pimpl->setDependencies( _pimpl->product().deps[Dep::PREREQUIRES], value );
00420 }
00421 else if ( key == "REQUIRES" )
00422 {
00423 _pimpl->setDependencies( _pimpl->product().deps[Dep::REQUIRES], value );
00424 }
00425 else if ( key == "PROVIDES" )
00426 {
00427 _pimpl->setDependencies( _pimpl->product().deps[Dep::PROVIDES], value );
00428 }
00429 else if ( key == "CONFLICTS" )
00430 {
00431 _pimpl->setDependencies( _pimpl->product().deps[Dep::CONFLICTS], value );
00432 }
00433 else if ( key == "OBSOLETES" )
00434 {
00435 _pimpl->setDependencies( _pimpl->product().deps[Dep::OBSOLETES], value );
00436 }
00437 else if ( key == "RECOMMENDS" )
00438 {
00439 _pimpl->setDependencies( _pimpl->product().deps[Dep::RECOMMENDS], value );
00440 }
00441 else if ( key == "SUGGESTS" )
00442 {
00443 _pimpl->setDependencies( _pimpl->product().deps[Dep::SUGGESTS], value );
00444 }
00445 else if ( key == "SUPPLEMENTS" )
00446 {
00447 _pimpl->setDependencies( _pimpl->product().deps[Dep::SUPPLEMENTS], value );
00448 }
00449 else if ( key == "ENHANCES" )
00450 {
00451 _pimpl->setDependencies( _pimpl->product().deps[Dep::ENHANCES], value );
00452 }
00453
00454
00455
00456 else if ( key == "DEFAULTBASE" )
00457 {
00458 _pimpl->repoindex().defaultBase = Arch(value);
00459 }
00460 else if ( key == "DESCRDIR" )
00461 {
00462 _pimpl->repoindex().descrdir = value;
00463 }
00464 else if ( key == "DATADIR" )
00465 {
00466 _pimpl->repoindex().datadir = value;
00467 }
00468 else if ( key == "FLAGS" )
00469 {
00470 str::split( value, std::back_inserter( _pimpl->repoindex().flags ) );
00471 }
00472 else if ( key == "KEY" )
00473 {
00474 if ( _pimpl->setFileCheckSum( _pimpl->repoindex().signingKeys, value ) )
00475 {
00476 ZYPP_THROW( ParseException( errPrefix( line.lineNo(), "Expected [KEY algorithm checksum filename]", *line ) ) );
00477 }
00478 }
00479 else if ( key == "LANGUAGE" )
00480 {
00481 _pimpl->repoindex().language;
00482 }
00483 else if ( key == "LINGUAS" )
00484 {
00485 std::set<std::string> strval;
00486 str::split( value, std::inserter( strval, strval.end() ) );
00487 for ( std::set<std::string>::const_iterator it = strval.begin(); it != strval.end(); ++it )
00488 {
00489 _pimpl->repoindex().languages.push_back( Locale(*it) );
00490 }
00491 }
00492 else if ( key == "META" )
00493 {
00494 if ( _pimpl->setFileCheckSum( _pimpl->repoindex().metaFileChecksums, value ) )
00495 {
00496 ZYPP_THROW( ParseException( errPrefix( line.lineNo(), "Expected [algorithm checksum filename]", *line ) ) );
00497 }
00498 }
00499 else if ( key == "HASH" )
00500 {
00501 if ( _pimpl->setFileCheckSum( _pimpl->repoindex().mediaFileChecksums, value ) )
00502 {
00503 ZYPP_THROW( ParseException( errPrefix( line.lineNo(), "Expected [algorithm checksum filename]", *line ) ) );
00504 }
00505 }
00506 else if ( key == "TIMEZONE" )
00507 {
00508 _pimpl->repoindex().timezone = value;
00509 }
00510 else
00511 { WAR << errPrefix( line.lineNo(), "Unknown tag", *line ) << endl; }
00512
00513
00514 if ( ! ticks.set( input_r.stream().tellg() ) )
00515 userRequestedAbort( line.lineNo() );
00516 }
00517
00518
00519
00520
00521 if ( _pimpl->hasProduct() )
00522 {
00523
00524 if ( ! _pimpl->product().distributionName.empty() )
00525 {
00526 _pimpl->product().deps[Dep::PROVIDES].insert(
00527 capability::parse( ResTraits<Product>::kind,
00528 _pimpl->product().distributionName,
00529 Rel::EQ,
00530 _pimpl->product().distributionEdition ) );
00531 }
00532 }
00533 if ( ! ticks.toMax() )
00534 userRequestedAbort( line.lineNo() );
00535
00536 endParse();
00537 MIL << "Done parsing " << input_r << endl;
00538 }
00539
00541 }
00544 }
00547 }