00001
00002
00003
00004
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
00067
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
00120 try
00121 {
00122
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
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
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
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
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 }
00252
00253
00254
00255 try
00256 {
00257
00258
00259
00260 Arch sysarch( getZYpp()->architecture() );
00261 Arch prodarch( Arch_noarch );
00262
00263
00264
00265 std::map< std::string, std::list<std::string> >::const_iterator it = prodImpl->_arch.find( sysarch.asString() );
00266
00267
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 {
00278 continue;
00279 }
00280 if (arch.compare( prodarch ) > 0)
00281 {
00282 prodarch = arch;
00283 it = it1;
00284 }
00285 }
00286 }
00287
00288
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() );
00300 }
00301
00302 MIL << "Product arch is " << prodarch << endl;
00303
00304
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 {
00381 string val = *next;
00382 if (val.find_first_of("<>=") != string::npos)
00383 {
00384 if (++next != splitted.end())
00385 {
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 }
00431 }
00434 }