id based Edition
[platform/upstream/libzypp.git] / zypp / parser / susetags / ContentFileReader.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/parser/susetags/ContentFileReader.cc
10  *
11 */
12 #include <iostream>
13 #include <sstream>
14
15 #include "zypp/base/LogTools.h"
16 #include "zypp/base/String.h"
17 #include "zypp/base/IOStream.h"
18 #include "zypp/base/UserRequestException.h"
19 #include "zypp/parser/ParseException.h"
20
21 #include "zypp/parser/susetags/ContentFileReader.h"
22 #include "zypp/parser/susetags/RepoIndex.h"
23 #include "zypp/data/ResolvableData.h"
24 #include "zypp/CapFactory.h"
25
26 #include "zypp/ZConfig.h"
27
28 using std::endl;
29 #undef  ZYPP_BASE_LOGGER_LOGGROUP
30 #define ZYPP_BASE_LOGGER_LOGGROUP "parser::susetags"
31
32 ///////////////////////////////////////////////////////////////////
33 namespace zypp
34 { /////////////////////////////////////////////////////////////////
35   ///////////////////////////////////////////////////////////////////
36   namespace parser
37   { /////////////////////////////////////////////////////////////////
38     ///////////////////////////////////////////////////////////////////
39     namespace susetags
40     { /////////////////////////////////////////////////////////////////
41
42       ///////////////////////////////////////////////////////////////////
43       //
44       //        CLASS NAME : ContentFileReader::Impl
45       //
46       /** ContentFileReader implementation. */
47       struct ContentFileReader::Impl
48       {
49         public:
50           Impl( const ContentFileReader & parent_r )
51           : _parent( parent_r )
52           {}
53
54           data::Product & product()
55           {
56             if ( !_product )
57               _product = new data::Product;
58             return *_product;
59           }
60
61           RepoIndex & repoindex()
62           {
63             if ( !_repoindex )
64               _repoindex = new RepoIndex;
65             return *_repoindex;
66           }
67
68           bool hasProduct() const
69           { return _product; }
70
71           bool hasRepoIndex() const
72           { return _repoindex; }
73
74           data::Product_Ptr handoutProduct()
75           {
76             data::Product_Ptr ret;
77             ret.swap( _product );
78             _product = 0;
79             return ret;
80           }
81
82           RepoIndex_Ptr handoutRepoIndex()
83           {
84             RepoIndex_Ptr ret;
85             ret.swap( _repoindex );
86             _repoindex = 0;
87             return ret;
88           }
89
90         public:
91           bool isRel( const std::string & rel_r ) const
92           {
93             try
94             {
95               Rel( rel_r );
96               return true;
97             }
98             catch (...)
99             {}
100             return false;
101           }
102
103           bool setUrlList( std::list<Url> & list_r, const std::string & value ) const
104           {
105             bool errors = false;
106             std::list<std::string> urls;
107             if ( str::split( value, std::back_inserter(urls) ) )
108             {
109               for ( std::list<std::string>::const_iterator it = urls.begin();
110                     it != urls.end(); ++it )
111               {
112                 try
113                 {
114                   list_r.push_back( *it );
115                 }
116                 catch( const Exception & excpt_r )
117                 {
118                   WAR << *it << ": " << excpt_r << endl;
119                   errors = true;
120                 }
121               }
122             }
123             return errors;
124           }
125
126           void setDependencies( data::DependencyList & deplist_r, const std::string & value ) const
127           {
128             std::list<std::string> words;
129             str::split( value, std::back_inserter( words ) );
130
131             for ( std::list<std::string>::const_iterator it = words.begin();
132                   it != words.end(); ++it )
133             {
134               Resolvable::Kind kind( ResTraits<Package>::kind );
135
136               std::string name = *it;
137               std::string::size_type colon = name.find( ":" );
138               if ( colon != std::string::npos )
139               {
140                 std::string skind( name, 0, colon );
141                 name.erase( 0, colon+1 );
142
143                 if ( skind == ResTraits<Pattern>::kind )
144                   kind = ResTraits<Pattern>::kind;
145                 else if ( skind == ResTraits<Patch>::kind )
146                   kind = ResTraits<Patch>::kind;
147                 else if ( skind == ResTraits<Product>::kind )
148                   kind = ResTraits<Product>::kind;
149                 else if ( skind == ResTraits<Selection>::kind )
150                   kind = ResTraits<Selection>::kind;
151                 else if ( skind != ResTraits<Package>::kind )
152                 {
153                   // colon but no kind ==> colon in a name
154                   name = skind + ":" + name;
155                 }
156               }
157
158               // check for Rel:
159               std::list<std::string>::const_iterator next = it;
160               if ( ++next != words.end()
161                    && (*next).find_first_of( "<>=" ) != std::string::npos )
162               {
163                 std::string op = *next;
164                 if ( ++next != words.end() )
165                 {
166                   name += " ";
167                   name += op;
168                   name += " ";
169                   name += *next;
170                   it = next;
171                 }
172               }
173
174               // Add the dependency
175               deplist_r.insert( capability::parse( kind, name ) );
176             }
177           }
178
179           bool setFileCheckSum( std::map<std::string, CheckSum> & map_r, const std::string & value ) const
180           {
181             bool error = false;
182             std::vector<std::string> words;
183             if ( str::split( value, std::back_inserter( words ) ) == 3 )
184             {
185               map_r[words[2]] = CheckSum( words[0], words[1] );
186             }
187             else
188             {
189               error = true;
190             }
191             return error;
192           }
193
194         public:
195           std::string _inputname;
196
197         private:
198           const ContentFileReader & _parent;
199           data::Product_Ptr  _product;
200           RepoIndex_Ptr      _repoindex;
201       };
202       ///////////////////////////////////////////////////////////////////
203
204       ///////////////////////////////////////////////////////////////////
205       //
206       //        CLASS NAME : ContentFileReader
207       //
208       ///////////////////////////////////////////////////////////////////
209
210       ///////////////////////////////////////////////////////////////////
211       //
212       //        METHOD NAME : ContentFileReader::ContentFileReader
213       //        METHOD TYPE : Ctor
214       //
215       ContentFileReader::ContentFileReader()
216       {}
217
218       ///////////////////////////////////////////////////////////////////
219       //
220       //        METHOD NAME : ContentFileReader::~ContentFileReader
221       //        METHOD TYPE : Dtor
222       //
223       ContentFileReader::~ContentFileReader()
224       {}
225
226       ///////////////////////////////////////////////////////////////////
227       //
228       //        METHOD NAME : ContentFileReader::beginParse
229       //        METHOD TYPE : void
230       //
231       void ContentFileReader::beginParse()
232       {
233         _pimpl.reset( new Impl(*this) );
234       }
235
236       ///////////////////////////////////////////////////////////////////
237       //
238       //        METHOD NAME : ContentFileReader::endParse
239       //        METHOD TYPE : void
240       //
241       void ContentFileReader::endParse()
242       {
243         // consume oldData
244         if ( _pimpl->hasProduct() )
245         {
246           if ( _productConsumer )
247             _productConsumer( _pimpl->handoutProduct() );
248         }
249         if ( _pimpl->hasRepoIndex() )
250         {
251           if ( _repoIndexConsumer )
252             _repoIndexConsumer( _pimpl->handoutRepoIndex() );
253         }
254
255         MIL << "[Content]" << endl;
256         _pimpl.reset();
257       }
258
259       ///////////////////////////////////////////////////////////////////
260       //
261       //        METHOD NAME : ContentFileReader::userRequestedAbort
262       //        METHOD TYPE : void
263       //
264       void ContentFileReader::userRequestedAbort( unsigned lineNo_r )
265       {
266         ZYPP_THROW( AbortRequestException( errPrefix( lineNo_r ) ) );
267       }
268
269       ///////////////////////////////////////////////////////////////////
270       //
271       //        METHOD NAME : ContentFileReader::errPrefix
272       //        METHOD TYPE : std::string
273       //
274       std::string ContentFileReader::errPrefix( unsigned lineNo_r,
275                                                 const std::string & msg_r,
276                                                 const std::string & line_r ) const
277       {
278         return str::form( "%s:%u:%s | %s",
279                           _pimpl->_inputname.c_str(),
280                           lineNo_r,
281                           line_r.c_str(),
282                           msg_r.c_str() );
283       }
284
285       ///////////////////////////////////////////////////////////////////
286       //
287       //        METHOD NAME : ContentFileReader::parse
288       //        METHOD TYPE : void
289       //
290       void ContentFileReader::parse( const InputStream & input_r,
291                                      const ProgressData::ReceiverFnc & fnc_r )
292       {
293         MIL << "Start parsing " << input_r << endl;
294         if ( ! input_r.stream() )
295         {
296           std::ostringstream s;
297           s << "Can't read bad stream: " << input_r;
298           ZYPP_THROW( ParseException( s.str() ) );
299         }
300         beginParse();
301         _pimpl->_inputname = input_r.name();
302
303         ProgressData ticks( makeProgressData( input_r ) );
304         ticks.sendTo( fnc_r );
305         if ( ! ticks.toMin() )
306           userRequestedAbort( 0 );
307
308         Arch sysarch( ZConfig::instance().systemArchitecture() );
309
310         iostr::EachLine line( input_r );
311         for( ; line; line.next() )
312         {
313           // strip 1st word from line to separate tag and value.
314           std::string value( *line );
315           std::string key( str::stripFirstWord( value, /*ltrim_first*/true ) );
316
317           if ( key.empty() || *key.c_str() == '#' ) // empty or comment line
318           {
319             continue;
320           }
321
322           // strip modifier if exists
323           std::string modifier;
324           std::string::size_type pos = key.rfind( '.' );
325           if ( pos != std::string::npos )
326           {
327             modifier = key.substr( pos+1 );
328             key.erase( pos );
329           }
330
331           //
332           // Product related data:
333           //
334           if ( key == "PRODUCT" )
335           {
336             std::replace( value.begin(), value.end(), ' ', '_' );
337             _pimpl->product().name = value;
338           }
339           else if ( key == "VERSION" )
340           {
341             _pimpl->product().edition = Edition( value );
342           }
343           else if ( key == "ARCH" )
344           {
345             // Default product arch is noarch. We update, if the
346             // ARCH.xxx tag is better than the current product arch
347             // and still compatible with the sysarch.
348             Arch carch( modifier );
349             if ( Arch::compare( _pimpl->product().arch, carch ) < 0
350                  &&  carch.compatibleWith( sysarch ) )
351             {
352               _pimpl->product().arch = carch;
353             }
354           }
355           else if ( key == "DISTPRODUCT" )
356           {
357             _pimpl->product().distributionName = value;
358           }
359           else if ( key == "DISTVERSION" )
360           {
361             _pimpl->product().distributionEdition = Edition( value );
362           }
363           else if ( key == "VENDOR" )
364           {
365             _pimpl->product().vendor = value;
366           }
367           else if ( key == "LABEL" )
368           {
369             _pimpl->product().summary.setText( value, Locale(modifier) );
370           }
371           else if ( key == "SHORTLABEL" )
372           {
373             _pimpl->product().shortName.setText( value, Locale(modifier) );
374           }
375           else if ( key == "TYPE" )
376           {
377             _pimpl->product().type = value;
378           }
379           else if ( key == "RELNOTESURL" )
380           {
381             for( std::string::size_type pos = value.find("%a");
382                  pos != std::string::npos;
383                  pos = value.find("%a") )
384             {
385               value.replace( pos, 2, sysarch.asString() );
386             }
387             try
388             {
389               _pimpl->product().releasenotesUrl = value;
390             }
391             catch( const Exception & excpt_r )
392             {
393               WAR << errPrefix( line.lineNo(), excpt_r.asString(), *line ) << endl;
394             }
395           }
396           else if ( key == "UPDATEURLS" )
397           {
398             if ( _pimpl->setUrlList( _pimpl->product().updateUrls, value ) )
399             {
400               WAR << errPrefix( line.lineNo(), "Ignored malformed URL(s)", *line ) << endl;
401             }
402           }
403           else if ( key == "EXTRAURLS" )
404           {
405             if ( _pimpl->setUrlList( _pimpl->product().extraUrls, value ) )
406             {
407               WAR << errPrefix( line.lineNo(), "Ignored malformed URL(s)", *line ) << endl;
408             }
409           }
410           else if ( key == "OPTIONALURLS" )
411           {
412             if ( _pimpl->setUrlList( _pimpl->product().optionalUrls, value ) )
413             {
414               WAR << errPrefix( line.lineNo(), "Ignored malformed URL(s)", *line ) << endl;
415             }
416           }
417           else if ( key == "PREREQUIRES" )
418           {
419             _pimpl->setDependencies( _pimpl->product().deps[Dep::PREREQUIRES], value );
420           }
421           else if ( key == "REQUIRES" )
422           {
423             _pimpl->setDependencies( _pimpl->product().deps[Dep::REQUIRES], value );
424           }
425           else if ( key == "PROVIDES" )
426           {
427             _pimpl->setDependencies( _pimpl->product().deps[Dep::PROVIDES], value );
428           }
429           else if ( key == "CONFLICTS" )
430           {
431             _pimpl->setDependencies( _pimpl->product().deps[Dep::CONFLICTS], value );
432           }
433           else if ( key == "OBSOLETES" )
434           {
435             _pimpl->setDependencies( _pimpl->product().deps[Dep::OBSOLETES], value );
436           }
437           else if ( key == "RECOMMENDS" )
438           {
439             _pimpl->setDependencies( _pimpl->product().deps[Dep::RECOMMENDS], value );
440           }
441           else if ( key == "SUGGESTS" )
442           {
443             _pimpl->setDependencies( _pimpl->product().deps[Dep::SUGGESTS], value );
444           }
445           else if ( key == "SUPPLEMENTS" )
446           {
447             _pimpl->setDependencies( _pimpl->product().deps[Dep::SUPPLEMENTS], value );
448           }
449           else if ( key == "ENHANCES" )
450           {
451             _pimpl->setDependencies( _pimpl->product().deps[Dep::ENHANCES], value );
452           }
453           //
454           // ReppoIndex related data:
455           //
456           else if ( key == "DEFAULTBASE" )
457           {
458             _pimpl->repoindex().defaultBase = Arch(value);
459           }
460           else if ( key == "DESCRDIR" )
461           {
462             _pimpl->repoindex().descrdir = value;
463           }
464           else if ( key == "DATADIR" )
465           {
466             _pimpl->repoindex().datadir = value;
467           }
468           else if ( key == "FLAGS" )
469           {
470             str::split( value, std::back_inserter( _pimpl->repoindex().flags ) );
471           }
472           else if ( key == "KEY" )
473           {
474             if ( _pimpl->setFileCheckSum( _pimpl->repoindex().signingKeys, value ) )
475             {
476               ZYPP_THROW( ParseException( errPrefix( line.lineNo(), "Expected [KEY algorithm checksum filename]", *line ) ) );
477             }
478           }
479           else if ( key == "LANGUAGE" )
480           {
481             _pimpl->repoindex().language;
482           }
483           else if ( key == "LINGUAS" )
484           {
485             std::set<std::string> strval;
486             str::split( value, std::inserter( strval, strval.end() ) );
487             for ( std::set<std::string>::const_iterator it = strval.begin(); it != strval.end(); ++it )
488             {
489               _pimpl->repoindex().languages.push_back( Locale(*it) );
490             }
491           }
492           else if ( key == "META" )
493           {
494             if ( _pimpl->setFileCheckSum( _pimpl->repoindex().metaFileChecksums, value ) )
495             {
496               ZYPP_THROW( ParseException( errPrefix( line.lineNo(), "Expected [algorithm checksum filename]", *line ) ) );
497             }
498           }
499           else if ( key == "HASH" )
500           {
501             if ( _pimpl->setFileCheckSum( _pimpl->repoindex().mediaFileChecksums, value ) )
502             {
503               ZYPP_THROW( ParseException( errPrefix( line.lineNo(), "Expected [algorithm checksum filename]", *line ) ) );
504             }
505           }
506           else if ( key == "TIMEZONE" )
507           {
508             _pimpl->repoindex().timezone = value;
509           }
510           else
511           { WAR << errPrefix( line.lineNo(), "Unknown tag", *line ) << endl; }
512
513
514           if ( ! ticks.set( input_r.stream().tellg() ) )
515             userRequestedAbort( line.lineNo() );
516         }
517
518         //
519         // post processing
520         //
521         if ( _pimpl->hasProduct() )
522         {
523           // Insert a "Provides" _dist_name" == _dist_version"
524           if ( ! _pimpl->product().distributionName.empty() )
525           {
526             _pimpl->product().deps[Dep::PROVIDES].insert(
527                 capability::parse( ResTraits<Product>::kind,
528                                    _pimpl->product().distributionName,
529                                    Rel::EQ,
530                                    _pimpl->product().distributionEdition ) );
531           }
532         }
533         if ( ! ticks.toMax() )
534           userRequestedAbort( line.lineNo() );
535
536         endParse();
537         MIL << "Done parsing " << input_r << endl;
538       }
539
540       /////////////////////////////////////////////////////////////////
541     } // namespace susetags
542     ///////////////////////////////////////////////////////////////////
543     /////////////////////////////////////////////////////////////////
544   } // namespace parser
545   ///////////////////////////////////////////////////////////////////
546   /////////////////////////////////////////////////////////////////
547 } // namespace zypp
548 ///////////////////////////////////////////////////////////////////