Imported Upstream version 14.45.0
[platform/upstream/libzypp.git] / zypp / Pathname.cc
1 /*---------------------------------------------------------------------\
2  |                          ____ _   __ __ ___                          |
3  |                         |__  / \ / / . \ . \                         |
4  |                           / / \ V /|  _/  _/                         |
5  |                          / /__ | | | | | |                           |
6  |                         /_____||_| |_| |_|                           |
7  |                                                                      |
8  \---------------------------------------------------------------------*/
9 /** \file       zypp/Pathname.cc
10  *
11 */
12 #include <iostream>
13
14 #include "zypp/base/String.h"
15 #include "zypp/Pathname.h"
16 #include "zypp/Url.h"
17
18 using std::string;
19
20 ///////////////////////////////////////////////////////////////////
21 namespace zypp
22 { /////////////////////////////////////////////////////////////////
23   ///////////////////////////////////////////////////////////////////
24   namespace filesystem
25   { /////////////////////////////////////////////////////////////////
26
27     ///////////////////////////////////////////////////////////////////
28     //
29     //  METHOD NAME : Pathname::_assign
30     //  METHOD TYPE : void
31     //
32     void Pathname::_assign( const string & name_r )
33     {
34       _name.clear();
35       if ( name_r.empty() )
36         return;
37       _name.reserve( name_r.size() );
38
39       // Collect up to "/.."
40       enum Pending {
41         P_none  = 0,    // ""
42         P_slash = 1,    // "/"
43         P_dot1  = 2,    // "/."
44         P_dot2  = 3     // "/.."
45       } pending = P_none;
46
47       // Assert relative path starting with "./"
48       // We rely on this below!
49       if ( name_r[0] != '/' )
50       {
51         _name += '.';
52         pending = P_slash;
53       }
54
55       // Lambda handling the "/.." case:
56       // []      + "/.."  ==> []
57       // [.]     + "/.."  ==> [./..]
58       // [foo]   is always [./foo] due to init above
59       // [*/..]  + "/.."  ==> [*/../..]
60       // [*/foo] + "/.."  ==> [*]
61       auto goParent_f =  [&](){
62         if ( _name.empty() )
63           /*NOOP*/;
64         else if ( _name.size() == 1 ) // content is '.'
65           _name += "/..";
66         else
67         {
68           std::string::size_type pos = _name.rfind( "/" );
69           if ( pos == _name.size() - 3 && _name[pos+1] == '.' && _name[pos+2] == '.' )
70             _name += "/..";
71           else
72             _name.erase( pos );
73         }
74       };
75
76       for ( auto ch : name_r )
77       {
78         switch ( ch )
79         {
80           case '/':
81             switch ( pending )
82             {
83               case P_none:      pending = P_slash; break;
84               case P_slash:     break;
85               case P_dot1:      pending = P_slash; break;
86               case P_dot2:      goParent_f(); pending = P_slash; break;
87             }
88             break;
89
90           case '.':
91             switch ( pending )
92             {
93               case P_none:      _name += '.'; break;
94               case P_slash:     pending = P_dot1; break;
95               case P_dot1:      pending = P_dot2; break;
96               case P_dot2:      _name += "/..."; pending = P_none; break;
97             }
98             break;
99
100           default:
101             switch ( pending )
102             {
103               case P_none:      break;
104               case P_slash:     _name += '/';    pending = P_none; break;
105               case P_dot1:      _name += "/.";   pending = P_none; break;
106               case P_dot2:      _name += "/.."; pending = P_none; break;
107             }
108             _name += ch;
109             break;
110         }
111       }
112
113       switch ( pending )
114       {
115         case P_none:    break;
116         case P_slash:   if ( _name.empty() ) _name = "/"; break;
117         case P_dot1:    if ( _name.empty() ) _name = "/"; break;
118         case P_dot2:    goParent_f(); if ( _name.empty() ) _name = "/"; break;
119       }
120       return;
121     }
122
123     ///////////////////////////////////////////////////////////////////
124     //
125     //  METHOD NAME : Pathname::dirname
126     //  METHOD TYPE : Pathname
127     //
128     Pathname Pathname::dirname( const Pathname & name_r )
129     {
130       if ( name_r.empty() )
131         return Pathname();
132
133       Pathname ret_t( name_r );
134       string::size_type idx = ret_t._name.find_last_of( '/' );
135
136       if ( idx == string::npos ) {
137         ret_t._name = ".";
138       } else if ( idx == 0 ) {
139         ret_t._name = "/";
140       } else {
141         ret_t._name.erase( idx );
142       }
143
144       return ret_t;
145     }
146
147     ///////////////////////////////////////////////////////////////////
148     //
149     //  METHOD NAME : Pathname::basename
150     //  METHOD TYPE : string
151     //
152     string Pathname::basename( const Pathname & name_r )
153     {
154       if ( name_r.empty() )
155         return string();
156
157       string ret_t( name_r.asString() );
158       string::size_type idx = ret_t.find_last_of( '/' );
159       if ( idx != string::npos && ( idx != 0 || ret_t.size() != 1 ) ) {
160         ret_t.erase( 0, idx+1 );
161       }
162
163       return ret_t;
164     }
165
166     ///////////////////////////////////////////////////////////////////
167     //
168     //  METHOD NAME : Pathname::asUrl
169     //  METHOD TYPE : Url
170     //
171     Url Pathname::asUrl( const std::string & scheme_r ) const
172     {
173       Url ret;
174       ret.setPathName( asString() );
175       ret.setScheme( scheme_r );
176       return ret;
177     }
178
179     Url Pathname::asUrl() const
180     { return asUrl( "dir" ); }
181
182     Url Pathname::asDirUrl() const
183     { return asUrl( "dir" ); }
184
185     Url Pathname::asFileUrl() const
186     { return asUrl( "file" ); }
187
188
189     std::string Pathname::showRoot( const Pathname & root_r, const Pathname & path_r )
190     {
191       return str::Str() << "(" << root_r << ")" << path_r;
192     }
193
194     std::string Pathname::showRootIf( const Pathname & root_r, const Pathname & path_r )
195     {
196       if ( root_r.empty() || root_r == "/" )
197         return path_r.asString();
198       return showRoot( root_r, path_r );
199     }
200
201     ///////////////////////////////////////////////////////////////////
202     //
203     //  METHOD NAME : Pathname::extension
204     //  METHOD TYPE : string
205     //
206     string Pathname::extension( const Pathname & name_r )
207     {
208       if ( name_r.empty() )
209         return string();
210
211       string base( basename( name_r ) );
212       string::size_type pos = base.rfind( '.' );
213       switch ( pos )
214       {
215         case 0:
216           if ( base.size() == 1 )                       // .
217             return string();
218           break;
219         case 1:
220           if ( base.size() == 2 && base[0] == '.' )     // ..
221             return string();
222           break;
223         case string::npos:
224           return string();
225           break;
226       }
227       return base.substr( pos );
228     }
229
230     ///////////////////////////////////////////////////////////////////
231     //
232     //  METHOD NAME : Pathname::assertprefix
233     //  METHOD TYPE : Pathname
234     //
235     Pathname Pathname::assertprefix( const Pathname & root_r, const Pathname & path_r )
236     {
237       if ( root_r.empty()
238            || path_r == root_r
239            || str::hasPrefix( path_r.asString(), root_r.asString() ) )
240         return path_r;
241       return root_r / path_r;
242     }
243
244     ///////////////////////////////////////////////////////////////////
245     //
246     //  METHOD NAME : Pathname::cat
247     //  METHOD TYPE : Pathname
248     //
249     Pathname Pathname::cat( const Pathname & name_r, const Pathname & add_tv )
250     {
251       if ( add_tv.empty() )
252         return name_r;
253       if ( name_r.empty() )
254         return add_tv;
255
256       string ret_ti( name_r._name );
257       if( add_tv._name[0] != '/' )
258         ret_ti += '/';
259       return ret_ti + add_tv._name;
260     }
261
262     ///////////////////////////////////////////////////////////////////
263     //
264     //  METHOD NAME : Pathname::Extend
265     //  METHOD TYPE : Pathname
266     //
267     Pathname Pathname::extend( const Pathname & l, const string & r )
268     {
269       return l.asString() + r;
270     }
271
272     /////////////////////////////////////////////////////////////////
273   } // namespace filesystem
274   ///////////////////////////////////////////////////////////////////
275   /////////////////////////////////////////////////////////////////
276 } // namespace zypp
277 ///////////////////////////////////////////////////////////////////