Imported Upstream version 14.45.0
[platform/upstream/libzypp.git] / zypp / parser / RepoindexFileReader.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/parser/RepoindexFileReader.cc
10  * Implementation of repoindex.xml file reader.
11  */
12 #include <iostream>
13 #include <unordered_map>
14
15 #include "zypp/base/String.h"
16 #include "zypp/base/Logger.h"
17 #include "zypp/base/Gettext.h"
18 #include "zypp/base/InputStream.h"
19
20 #include "zypp/Pathname.h"
21
22 #include "zypp/parser/xml/Reader.h"
23 #include "zypp/parser/ParseException.h"
24
25 #include "zypp/RepoInfo.h"
26
27 #include "zypp/parser/RepoindexFileReader.h"
28
29
30 #undef ZYPP_BASE_LOGGER_LOGGROUP
31 #define ZYPP_BASE_LOGGER_LOGGROUP "parser"
32
33 using std::endl;
34
35 namespace zypp
36 {
37   namespace parser
38   {
39     using xml::Reader;
40     using xml::XmlString;
41
42     ///////////////////////////////////////////////////////////////////
43     namespace
44     {
45       class VarReplacer : private base::NonCopyable
46       {
47       public:
48         /** */
49         void setVar( const std::string & key_r, const std::string & val_r )
50         {
51           MIL << "*** Inject " << key_r << " = " << val_r;
52           _vars[key_r] = replace( val_r );
53           MIL << " (" << _vars[key_r] << ")" << endl;
54         }
55
56         std::string replace( const std::string & val_r ) const
57         {
58           std::string::size_type vbeg = val_r.find( "%{", 0 );
59           if ( vbeg == std::string::npos )
60             return val_r;
61
62           str::Str ret;
63           std::string::size_type cbeg = 0;
64           for( ; vbeg != std::string::npos; vbeg = val_r.find( "%{", vbeg ) )
65           {
66             std::string::size_type nbeg = vbeg+2;
67             std::string::size_type nend = val_r.find( "}", nbeg );
68             if ( nend == std::string::npos )
69             {
70               WAR << "Incomplete variable in '" << val_r << "'" << endl;
71               break;
72             }
73             const auto & iter = _vars.find( val_r.substr( nbeg, nend-nbeg ) );
74             if ( iter != _vars.end() )
75             {
76               if ( cbeg < vbeg )
77                 ret << val_r.substr( cbeg, vbeg-cbeg );
78               ret << iter->second;
79               cbeg = nend+1;
80             }
81             else
82               WAR << "Undefined variable %{" << val_r.substr( nbeg, nend-nbeg ) << "} in '" << val_r << "'" << endl;
83             vbeg = nend+1;
84           }
85           if ( cbeg < val_r.size() )
86             ret << val_r.substr( cbeg );
87
88           return ret;
89         }
90       private:
91         std::unordered_map<std::string,std::string> _vars;
92       };
93     } // namespace
94     ///////////////////////////////////////////////////////////////////
95
96   ///////////////////////////////////////////////////////////////////////
97   //
98   //  CLASS NAME : RepoindexFileReader::Impl
99   //
100   class RepoindexFileReader::Impl : private base::NonCopyable
101   {
102   public:
103     /**
104      * CTOR
105      *
106      * \see RepoindexFileReader::RepoindexFileReader(Pathname,ProcessResource)
107      */
108     Impl(const InputStream &is, const ProcessResource & callback);
109
110     /**
111      * Callback provided to the XML parser.
112      */
113     bool consumeNode( Reader & reader_r );
114
115   private:
116     bool getAttrValue( const std::string & key_r, Reader & reader_r, std::string & value_r )
117     {
118       const XmlString & s( reader_r->getAttribute( key_r ) );
119       if ( s.get() )
120       {
121         value_r = _replacer.replace( s.asString() );
122         return !value_r.empty();
123       }
124       value_r.clear();
125       return false;
126     }
127
128   private:
129     /** Function for processing collected data. Passed-in through constructor. */
130     ProcessResource _callback;
131     VarReplacer _replacer;
132   };
133   ///////////////////////////////////////////////////////////////////////
134
135   RepoindexFileReader::Impl::Impl(const InputStream &is,
136                                   const ProcessResource & callback)
137     : _callback(callback)
138   {
139     Reader reader( is );
140     MIL << "Reading " << is.path() << endl;
141     reader.foreachNode( bind( &RepoindexFileReader::Impl::consumeNode, this, _1 ) );
142   }
143
144   // --------------------------------------------------------------------------
145
146   /*
147    * xpath and multiplicity of processed nodes are included in the code
148    * for convenience:
149    *
150    * // xpath: <xpath> (?|*|+)
151    *
152    * if multiplicity is ommited, then the node has multiplicity 'one'.
153    */
154
155   // --------------------------------------------------------------------------
156
157   bool RepoindexFileReader::Impl::consumeNode( Reader & reader_r )
158   {
159     if ( reader_r->nodeType() == XML_READER_TYPE_ELEMENT )
160     {
161       // xpath: /repoindex
162       if ( reader_r->name() == "repoindex" )
163       {
164         while ( reader_r.nextNodeAttribute() )
165           _replacer.setVar( reader_r->localName().asString(), reader_r->value().asString() );
166         return true;
167       }
168
169       // xpath: /repoindex/data (+)
170       if ( reader_r->name() == "repo" )
171       {
172         RepoInfo info;
173         // Set some defaults that are not contained in the repo information
174         info.setAutorefresh( true );
175         info.setEnabled(false);
176
177         std::string attrValue;
178
179         // required alias
180         // mandatory, so we can allow it in var replacement without reset
181         if ( getAttrValue( "alias", reader_r, attrValue ) )
182         {
183           info.setAlias( attrValue );
184           _replacer.setVar( "alias", attrValue );
185         }
186         else
187           throw ParseException(str::form(_("Required attribute '%s' is missing."), "alias"));
188
189         // required url
190         // SLES HACK: or path, but beware of the hardcoded '/repo' prefix!
191         {
192           std::string urlstr;
193           std::string pathstr;
194           getAttrValue( "url", reader_r, urlstr );
195           getAttrValue( "path", reader_r, pathstr );
196           if ( urlstr.empty() )
197           {
198             if ( pathstr.empty() )
199               throw ParseException(str::form(_("One or both of '%s' or '%s' attributes is required."), "url", "path"));
200             else
201               info.setPath( Pathname("/repo") / pathstr );
202           }
203           else
204           {
205             if ( pathstr.empty() )
206               info.setBaseUrl( Url(urlstr) );
207             else
208             {
209               Url url( urlstr );
210               url.setPathName( Pathname(url.getPathName()) / "repo" / pathstr );
211               info.setBaseUrl( url );
212             }
213           }
214         }
215
216         // optional name
217         if ( getAttrValue( "name", reader_r, attrValue ) )
218           info.setName( attrValue );
219
220         // optional targetDistro
221         if ( getAttrValue( "distro_target", reader_r, attrValue ) )
222           info.setTargetDistribution( attrValue );
223
224         // optional priority
225         if ( getAttrValue( "priority", reader_r, attrValue ) )
226           info.setPriority( str::strtonum<unsigned>( attrValue ) );
227
228
229         // optional enabled
230         if ( getAttrValue( "enabled", reader_r, attrValue ) )
231           info.setEnabled( str::strToBool( attrValue, info.enabled() ) );
232
233         // optional autorefresh
234         if ( getAttrValue( "autorefresh", reader_r, attrValue ) )
235           info.setAutorefresh( str::strToBool( attrValue, info.autorefresh() ) );
236
237         DBG << info << endl;
238
239         // ignore the rest
240         _callback(info);
241         return true;
242       }
243     }
244
245     return true;
246   }
247
248
249   ///////////////////////////////////////////////////////////////////
250   //
251   //  CLASS NAME : RepoindexFileReader
252   //
253   ///////////////////////////////////////////////////////////////////
254
255   RepoindexFileReader::RepoindexFileReader(
256       const Pathname & repoindex_file, const ProcessResource & callback)
257     :
258       _pimpl(new Impl(InputStream(repoindex_file), callback))
259   {}
260
261   RepoindexFileReader::RepoindexFileReader(
262        const InputStream &is, const ProcessResource & callback )
263     : _pimpl(new Impl(is, callback))
264   {}
265
266   RepoindexFileReader::~RepoindexFileReader()
267   {}
268
269
270   } // ns parser
271 } // ns zypp
272
273 // vim: set ts=2 sts=2 sw=2 et ai: