ProductMetadataParser.cc

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------\
00002 |                          ____ _   __ __ ___                          |
00003 |                         |__  / \ / / . \ . \                         |
00004 |                           / / \ V /|  _/  _/                         |
00005 |                          / /__ | | | | | |                           |
00006 |                         /_____||_| |_| |_|                           |
00007 |                                                                      |
00008 \---------------------------------------------------------------------*/
00012 #include <iostream>
00013 #include <fstream>
00014 #include <sstream>
00015 
00016 #include <boost/tokenizer.hpp>
00017 #include <boost/algorithm/string.hpp>
00018 
00019 #include "zypp/ZYppFactory.h"
00020 #include "zypp/base/Logger.h"
00021 #include "zypp/base/Exception.h"
00022 #include "zypp/base/PtrTypes.h"
00023 #include "zypp/base/String.h"
00024 
00025 #include "zypp/CapFactory.h"
00026 
00027 #include "zypp/source/susetags/ProductMetadataParser.h"
00028 #include "zypp/source/susetags/SuseTagsProductImpl.h"
00029 #include <boost/regex.hpp>
00030 
00031 #undef ZYPP_BASE_LOGGER_LOGGROUP
00032 #define ZYPP_BASE_LOGGER_LOGGROUP "ProductMetadataParser"
00033 
00034 using namespace std;
00035 using namespace boost;
00036 
00037 typedef find_iterator<string::iterator> string_find_iterator;
00038 
00040 namespace zypp
00041 { 
00042 
00043 namespace source
00044 { 
00045 
00046 namespace susetags
00047 {
00048 
00049 static void replace_variables( std::string &text )
00050 {
00051   string::size_type pos = text.find("%a");
00052   if (pos != string::npos)
00053   {
00054     Arch sysarch( getZYpp()->architecture() );
00055     text.replace( pos, 2, sysarch.asString() );
00056   }
00057 }
00058 
00059 ProductMetadataParser::ProductMetadataParser()
00060 {
00061   prodImpl = new SuseTagsProductImpl;
00062 }
00063 
00065 //
00066 //      METHOD NAME : Parser::parse
00067 //      METHOD TYPE : void
00068 //
00069 void ProductMetadataParser::parse( const Pathname & file_r, Source_Ref source_r )
00070 {
00071   std::ifstream file(file_r.asString().c_str());
00072 
00073   if (!file)
00074   {
00075     ZYPP_THROW (Exception("Bad source ["+ source_r.alias() +"] at URL:[" + source_r.url().asString() + "]. Can't open product file: [" + file_r.asString() + "]"));
00076   }
00077 
00078   std::string buffer;
00079   volatile_content = false;
00080   boost::regex e("^(([A-Z]+)(\\.([_A-Z0-9a-z]+)){0,1}) (.+)$");
00081   while (file && !file.eof())
00082   {
00083     getline(file, buffer);
00084     boost::smatch what;
00085     if (boost::regex_match(buffer, what, e, boost::match_extra))
00086     {
00087       if ( what.size() < 5 )
00088       {
00089         ZYPP_THROW (Exception("Corrupt source? ["+ source_r.alias() +"] at URL:[" + source_r.url().asString() + "]. Can't parse line: [" + buffer + "]"));
00090       }
00091 
00092       std::string key = what[2];
00093       std::string value = what[5];
00094       std::string modifier = what[4];
00095       if (key == "PRODUCT")
00096       {
00097         string s(value);
00098         std::replace(s.begin(), s.end(), ' ', '_');
00099         prodImpl->_name = s;
00100       }
00101       else if (key == "VERSION")
00102         prodImpl->_version = value;
00103       else if (key == "DISTPRODUCT")
00104         prodImpl->_dist_name = value;
00105       else if (key == "DISTVERSION")
00106         prodImpl->_dist_version = value;
00107       else if (key == "BASEPRODUCT")
00108         prodImpl->_base_product = value;
00109       else if (key == "BASEVERSION")
00110         prodImpl->_base_version = value;
00111       else if (key == "YOUTYPE")
00112         prodImpl->_you_type = value;
00113       else if (key == "VENDOR")
00114         prodImpl->_vendor = value;
00115       else if (key == "SHORTLABEL")
00116         prodImpl->_shortlabel = value;
00117       else if (key == "RELNOTESURL")
00118       {
00119         // Url class throws in case of invalid URL
00120         try
00121         {
00122           // replace variables like %a
00123           replace_variables(value);
00124 
00125           Url url (value) ;
00126           prodImpl->_release_notes_url = url;
00127         }
00128         catch ( ... )
00129         {
00130           prodImpl->_release_notes_url = Url();
00131         }
00132       }
00133       else if (key == "UPDATEURLS")
00134       {
00135         std::list<std::string> items;
00136         boost::algorithm::split(items, value, is_space());
00137         std::list<std::string>::const_iterator
00138         b = items.begin(),
00139             e = items.end(),
00140                 i;
00141         for (i = b; i != e; ++i)
00142         {
00143           // Url class throws in case of invalid URL
00144           try
00145           {
00146             Url url = *i;
00147             prodImpl->_update_urls.push_back( url );
00148           }
00149           catch ( ... )
00150           {
00151             ZYPP_THROW (Exception("Bad source ["+ source_r.alias() +"] at URL:[" + source_r.url().asString() + "]. Ilegal update Url: [" + *i + "]"));
00152           }
00153         }
00154       }
00155       else if (key == "EXTRAURLS")
00156       {
00157         std::list<std::string> items;
00158         boost::algorithm::split(items, value, is_space());
00159         std::list<std::string>::const_iterator
00160         b = items.begin(),
00161             e = items.end(),
00162                 i;
00163         for (i = b; i != e; ++i)
00164         {
00165           // Url class throws in case of invalid URL
00166           try
00167           {
00168             Url url = *i;
00169             prodImpl->_extra_urls.push_back( url );
00170           }
00171           catch ( ... )
00172           {
00173             ZYPP_THROW (Exception("Bad source ["+ source_r.alias() +"] at URL:[" + source_r.url().asString() + "]. Ilegal optional Url: [" + *i + "]"));
00174           }
00175         }
00176       }
00177       else if (key == "OPTIONALURLS")
00178       {
00179         std::list<std::string> items;
00180         boost::algorithm::split(items, value, is_space());
00181         std::list<std::string>::const_iterator
00182         b = items.begin(),
00183             e = items.end(),
00184                 i;
00185         for (i = b; i != e; ++i)
00186         {
00187           // Url class throws in case of invalid URL
00188           try
00189           {
00190             Url url = *i;
00191             prodImpl->_optional_urls.push_back( url );
00192           }
00193           catch ( ... )
00194           {
00195             ZYPP_THROW (Exception("Bad source ["+ source_r.alias() +"] at URL:[" + source_r.url().asString() + "]. Ilegal optional Url: [" + *i + "]"));
00196           }
00197         }
00198       }
00199       else if (key == "ARCH")
00200         parseLine( key, modifier, value, prodImpl->_arch);
00201       else if (key == "DEFAULTBASE")
00202         prodImpl->_default_base = value;
00203       else if (key == "PREREQUIRES")
00204         parseDependencies( key, value, prodImpl->_deps, Dep::PREREQUIRES);
00205       else if (key == "REQUIRES")
00206         parseDependencies( key, value, prodImpl->_deps, Dep::REQUIRES);
00207       else if (key == "PROVIDES")
00208         parseDependencies( key, value, prodImpl->_deps, Dep::PROVIDES);
00209       else if (key == "CONFLICTS")
00210         parseDependencies( key, value, prodImpl->_deps, Dep::CONFLICTS);
00211       else if (key == "OBSOLETES")
00212         parseDependencies( key, value, prodImpl->_deps, Dep::OBSOLETES);
00213       else if (key == "RECOMMENDS")
00214         parseDependencies( key, value, prodImpl->_deps, Dep::RECOMMENDS);
00215       else if (key == "SUGGESTS")
00216         parseDependencies( key, value, prodImpl->_deps, Dep::SUGGESTS);
00217       else if (key == "SUPPLEMENTS")
00218         parseDependencies( key, value, prodImpl->_deps, Dep::SUPPLEMENTS);
00219       else if (key == "ENHANCES")
00220         parseDependencies( key, value, prodImpl->_deps, Dep::ENHANCES);
00221       else if (key == "LINGUAS")
00222         parseLine( key, value, prodImpl->_languages);
00223       else if (key == "LABEL")
00224         parseLine( key, modifier, value, prodImpl->_summary);
00225       else if (key == "DESCRDIR")
00226         prodImpl->_description_dir = value;
00227       else if (key == "DATADIR")
00228         prodImpl->_data_dir = value;
00229       else if (key == "FLAGS")
00230         parseLine( key, value, prodImpl->_flags);
00231       else if (key == "LANGUAGE")
00232         prodImpl->_language = value;
00233       else if (key == "TIMEZONE")
00234         prodImpl->_timezone = value;
00235       else if (key == "META")
00236         parseFileCheckSum( key, value, prodImpl->_descr_files_checksums);
00237       else if (key == "KEY")
00238         parseFileCheckSum( key, value, prodImpl->_signing_keys);
00239       else if (key == "VOLATILE_CONTENT")
00240         volatile_content = true;
00241       else
00242       {
00243         WAR << "In source ["+ source_r.alias() +"] at URL:[" + source_r.url().asString() + "]. Unknown key: [" + key + "] with value [" + value + "]" << std::endl;
00244         //ZYPP_THROW (Exception("Corrupt source ["+ source_r.alias() +"] at URL:[" + source_r.url().asString() + "]. Unknown key: [" + key + "] with value [" + value + "]"));
00245       }
00246     }
00247     else if (!buffer.empty())
00248     {
00249       WAR << "Ignoring line [" << buffer << "] in source ["<< source_r.alias() << "] at URL:[" + source_r.url().asString() << "]." << std::endl;
00250     }
00251   } // end while
00252   // finished parsing, store result
00253   // Collect basic Resolvable data
00254 
00255   try
00256   {
00257     // calculate product architecture by looking through ARCH.xxx lines (key of prodImpl->_arch)
00258     //  and taking the 'best' (first) architectures.
00259 
00260     Arch sysarch( getZYpp()->architecture() );
00261     Arch prodarch( Arch_noarch );               // default to noarch
00262 
00263     // find matching ARCH.xxx line
00264 
00265     std::map< std::string, std::list<std::string> >::const_iterator it = prodImpl->_arch.find( sysarch.asString() );
00266 
00267     // if no matching ARCH.xxx line found, search best matching
00268 
00269     if (it == prodImpl->_arch.end())
00270     {
00271       WAR << "Product does not fully support systems architecture (" << sysarch << ")" << endl;
00272 
00273       for (std::map< std::string, std::list<std::string> >::const_iterator it1 = prodImpl->_arch.begin(); it1 != prodImpl->_arch.end(); ++it1)
00274       {
00275         Arch arch( it1->first );
00276         if (!arch.compatibleWith( sysarch ))
00277         {       // filter out incompatbile ones
00278           continue;
00279         }
00280         if (arch.compare( prodarch ) > 0)
00281         {       // found better than current
00282           prodarch = arch;                      //  set new current
00283           it = it1;
00284         }
00285       }
00286     }
00287 
00288     // oops, still no match found ?
00289 
00290     if (it == prodImpl->_arch.end()
00291         || it->second.empty())
00292     {
00293       ERR << "Product incompatible with systems architecture (" << sysarch << ")" << endl;
00294     }
00295     else
00296     {
00297       MIL << "Found matching/best arch " << it->first << endl;
00298 
00299       prodarch = Arch( it->second.front() );    // first arch of matching ARCH.xxx line is best
00300     }
00301 
00302     MIL << "Product arch is " << prodarch << endl;
00303 
00304     // Insert a "Provides" _dist_name" == _dist_version"
00305     CapFactory capfactory;
00306     prodImpl->_deps[Dep::PROVIDES].insert( capfactory.parse( ResTraits<Product>::kind,
00307                       prodImpl->_dist_name,
00308                       Rel::EQ,
00309                       Edition( prodImpl->_dist_version ) ) );
00310 
00311     NVRAD dataCollect( prodImpl->_name, Edition( prodImpl->_version ), prodarch, prodImpl->_deps );
00312     
00313     result = detail::makeResolvableFromImpl( dataCollect, prodImpl );
00314   }
00315   catch (const Exception & excpt_r)
00316   {
00317     ZYPP_THROW(Exception("Error creating product: " + excpt_r.msg()));
00318   }
00319 
00320   prodImpl->_source = source_r;
00321   prodImpl->_category = source_r.baseSource() ? "base" : "add-on";
00322   INT << "Product category set to " << prodImpl->_category << endl;
00323 }
00324 
00325 void ProductMetadataParser::parseLine( const string &key, const string &modif, const string &value, map< string, list<string> > &container)
00326 {
00327   if ( modif.size() == 0)
00328     parseLine( key, value, container["default"]);
00329   else
00330     parseLine( key, value, container[modif]);
00331 }
00332 
00333 void ProductMetadataParser::parseLine( const std::string &key, const std::string &lang, const std::string &value, TranslatedText &container)
00334 {
00335   if ( lang.size() == 0)
00336     container.setText(value, Locale());
00337   else
00338     container.setText(value, Locale(lang));
00339 }
00340 
00341 void ProductMetadataParser::parseLine( const string &key, const string &modif, const string &value, map< string, string > &container)
00342 {
00343   if ( modif.size() == 0)
00344     container["default"] = value;
00345   else
00346     container[modif] = value;
00347 }
00348 
00349 void ProductMetadataParser::parseLine( const string &key, const string &value, std::list<std::string> &container)
00350 {
00351   str::split( value, std::back_inserter(container), " ");
00352 }
00353 
00354 void ProductMetadataParser::parseDependencies( const string &key, const string &value, Dependencies & deps, Dep deptag )
00355 {
00356   std::list<std::string> splitted;
00357   str::split( value, std::back_inserter(splitted), " ");
00358   Resolvable::Kind kind;
00359   std::string name;
00360   CapFactory f;
00361   for (std::list<std::string>::const_iterator it = splitted.begin(); it != splitted.end(); ++it)
00362   {
00363     string name = *it;
00364     string::size_type colon = name.find(":");
00365     kind  = ResTraits<Package>::kind;
00366     if (colon != string::npos)
00367     {
00368       string skind( name, 0, colon );
00369       name.erase( 0, colon+1 );
00370       DBG << "kind " << skind << ", name " << name << endl;
00371       if (skind == "pattern") kind = ResTraits<Pattern>::kind;
00372       else if (skind == "patch") kind = ResTraits<Patch>::kind;
00373       else if (skind == "selection") kind = ResTraits<Selection>::kind;
00374       else if (skind == "product") kind = ResTraits<Product>::kind;
00375       else if (skind != "package") ERR << "Bad kind in content::" << key << " '" << skind << "'" << endl;
00376     }
00377     std::list<std::string>::const_iterator next = it;
00378     ++next;
00379     if (next != splitted.end())
00380     {                   // check for "op edition"
00381       string val = *next;
00382       if (val.find_first_of("<>=") != string::npos)     // next token is op
00383       {
00384         if (++next != splitted.end())
00385         {               // ensure edition follows
00386           name += " ";
00387           name += val;
00388           name += " ";
00389           name += *next;
00390           it = next;
00391         }
00392       }
00393     }
00394     DBG << "parsing[" << name << "]" << endl;
00395     try
00396     {
00397       deps[deptag].insert( f.parse( kind, name ) );
00398     }
00399     catch (Exception & excpt_r)
00400     {
00401       ZYPP_CAUGHT( excpt_r );
00402       ERR << "Ignoring invalid " << key << " entry '" << name << "'" << endl;
00403     }
00404   }
00405 }
00406 
00407 void ProductMetadataParser::parseFileCheckSum( const std::string &key, const std::string &value, std::map<std::string, CheckSum> &container)
00408 {
00409   std::list<std::string> splitted;
00410   str::split( value, std::back_inserter(splitted), " ");
00411   if (splitted.size() != 3)
00412   {
00413     ZYPP_THROW (Exception("Parse error in checksum entry. Expected [algorithm checksum filename], got [" + value + "]"));
00414   }
00415   else
00416   {
00417     std::string checksum_type = splitted.front();
00418     splitted.pop_front();
00419     std::string checksum_str = splitted.front();
00420     splitted.pop_front();
00421     std::string filename = splitted.front();
00422     splitted.pop_front();
00423     MIL << "Checksum for " << filename << " is " << checksum_str << " (" << checksum_type << ")" << std::endl;
00424     container[filename] = CheckSum(checksum_type, checksum_str);
00425   }
00426 }
00428 } // namespace susetags
00431 } // namespace source
00434 } // namespace zypp

Generated on Tue Nov 28 16:49:32 2006 for zypp by  doxygen 1.5.0