Allow backslash in file names. (bnc #561855)
[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     namespace
29     { /////////////////////////////////////////////////////////////////
30
31       ///////////////////////////////////////////////////////////////////
32       //
33       //        CLASS NAME : DirStack
34       //
35       /** silly helper to build Pathnames.
36       */
37       class DirStack {
38
39         struct Dir {
40
41           Dir *  up;
42           Dir *  dn;
43           string name;
44
45           Dir( const string & n = "" ) {
46             name = n;
47             up = dn = 0;
48           }
49
50           ~Dir() {
51             if ( up )
52               up->dn = dn;
53             if ( dn )
54               dn->up = up;
55           }
56         };
57
58         Dir *  top;
59         Dir *  bot;
60
61         void Pop() {
62           if ( !top )
63             return;
64           top = top->dn;
65           if ( top )
66             delete top->up;
67           else {
68             delete bot;
69             bot = 0;
70           }
71         }
72
73       public:
74
75         DirStack() { top = bot = 0; }
76         ~DirStack() {
77           while ( bot )
78             Pop();
79         }
80
81         void Push( const string & n ) {
82           if ( n.empty() || n == "." ) { // '.' or '/' only for bot
83             if ( bot )
84               return;
85           } else if ( n == ".." && top ) {
86             if ( top->name == "" )          // "/.."        ==> "/"
87               return;
88
89             if ( top->name != "." && top->name != ".." ) {      // "somedir/.." ==> ""
90               Pop();
91               return;
92             }
93             // "../.." "./.." stays
94           }
95
96           Dir * d = new Dir( n );
97           if ( !top )
98             top = bot = d;
99           else {
100             top->up = d;
101             d->dn = top;
102             d->up = 0;
103             top = d;
104           }
105         }
106
107         string str() {
108           if ( !bot )
109             return "";
110           string ret;
111           for ( Dir * d = bot; d; d = d->up ) {
112             if ( d != bot )
113               ret += "/";
114             ret += d->name;
115           }
116           if ( ret.empty() )
117             return "/";
118           return ret;
119         }
120       };
121
122       /////////////////////////////////////////////////////////////////
123     } // namespace
124     ///////////////////////////////////////////////////////////////////
125
126     ///////////////////////////////////////////////////////////////////
127     //
128     //  METHOD NAME : Pathname::_assign
129     //  METHOD TYPE : void
130     //
131     void Pathname::_assign( const string & name_tv )
132     {
133       prfx_i = 0;
134       name_t = name_tv;
135
136       if ( name_t.empty() )
137         return;
138
139       string   Tprfx;
140       DirStack Stack_Ci;
141
142       char *       Buf_aci    = new char[name_tv.length() + 1];
143       char *       W_pci      = Buf_aci;
144       const char * R_pci      = name_tv.c_str();
145
146       // check for prefix
147       if (    name_t.length() >= 2
148            && name_t[1] == ':'
149            && (    ( 'a' <= name_t[0] && name_t[0] <= 'z' )
150                 || ( 'A' <= name_t[0] && name_t[0] <= 'Z' ) ) ) {
151         Tprfx  = name_t.substr( 0, 2 );
152         prfx_i = 2;
153         R_pci += 2;
154       }
155
156       // rel or abs path
157       if ( *R_pci == '/' ) {
158         Stack_Ci.Push( "" );
159         ++R_pci;
160       } else {
161         Stack_Ci.Push( "." );
162       }
163
164       do {
165         switch ( *R_pci ) {
166         case '/':
167         case '\0':
168           if ( W_pci != Buf_aci ) {
169             *W_pci = '\0';
170             W_pci = Buf_aci;
171             Stack_Ci.Push( Buf_aci );
172           }
173           break;
174
175         default:
176           *W_pci++ = *R_pci;
177           break;
178         }
179       } while( *R_pci++ );
180
181       delete[] Buf_aci;
182       name_t = Tprfx + Stack_Ci.str();
183     }
184
185     ///////////////////////////////////////////////////////////////////
186     //
187     //  METHOD NAME : Pathname::dirname
188     //  METHOD TYPE : Pathname
189     //
190     Pathname Pathname::dirname( const Pathname & name_tv )
191     {
192       if ( name_tv.empty() )
193         return "";
194
195       Pathname ret_t( name_tv );
196       string::size_type idx = ret_t.name_t.find_last_of( '/' );
197
198       if ( idx == string::npos ) {
199         ret_t.name_t.erase( ret_t.prfx_i );
200         ret_t.name_t += ".";
201       } else if ( idx == ret_t.prfx_i ) {
202         ret_t.name_t.erase( ret_t.prfx_i );
203         ret_t.name_t += "/";
204       } else {
205         ret_t.name_t.erase( idx );
206       }
207
208       return ret_t;
209     }
210
211     ///////////////////////////////////////////////////////////////////
212     //
213     //  METHOD NAME : Pathname::basename
214     //  METHOD TYPE : string
215     //
216     string Pathname::basename( const Pathname & name_tv )
217     {
218       if ( name_tv.empty() )
219         return string();
220
221       string ret_t( name_tv.asString() );
222       ret_t.erase( 0, name_tv.prfx_i );
223       string::size_type idx = ret_t.find_last_of( '/' );
224       if ( idx != string::npos ) {
225         ret_t.erase( 0, idx+1 );
226       }
227
228       return ret_t;
229     }
230
231     ///////////////////////////////////////////////////////////////////
232     //
233     //  METHOD NAME : Pathname::asUrl
234     //  METHOD TYPE : Url
235     //
236     Url Pathname::asUrl() const
237     {
238       Url ret( "dir:///" );
239       ret.setPathName( asString() );
240       return ret;
241     }
242
243     std::string Pathname::showRoot( const Pathname & root_r, const Pathname & path_r )
244     {
245       return str::Str() << "(" << root_r << ")" << path_r;
246     }
247
248     std::string Pathname::showRootIf( const Pathname & root_r, const Pathname & path_r )
249     {
250       if ( root_r.empty() || root_r == "/" )
251         return path_r.asString();
252       return showRoot( root_r, path_r );
253     }
254
255     ///////////////////////////////////////////////////////////////////
256     //
257     //  METHOD NAME : Pathname::extension
258     //  METHOD TYPE : string
259     //
260     string Pathname::extension( const Pathname & name_tv )
261     {
262       if ( name_tv.empty() )
263         return string();
264
265       string base( basename( name_tv ) );
266       string::size_type pos = base.rfind( '.' );
267       if ( pos == string::npos )
268         return string();
269       return base.substr( pos );
270     }
271
272     ///////////////////////////////////////////////////////////////////
273     //
274     //  METHOD NAME : Pathname::assertprefix
275     //  METHOD TYPE : Pathname
276     //
277     Pathname Pathname::assertprefix( const Pathname & root_r, const Pathname & path_r )
278     {
279       if ( root_r.empty()
280            || path_r == root_r
281            || str::hasPrefix( path_r.asString(), root_r.asString() ) )
282         return path_r;
283       return root_r / path_r;
284     }
285
286     ///////////////////////////////////////////////////////////////////
287     //
288     //  METHOD NAME : Pathname::cat
289     //  METHOD TYPE : Pathname
290     //
291     Pathname Pathname::cat( const Pathname & name_tv, const Pathname & add_tv )
292     {
293       if ( add_tv.empty() )
294         return name_tv;
295       if ( name_tv.empty() )
296         return add_tv;
297
298       string ret_ti( add_tv.asString() );
299       ret_ti.replace( 0, add_tv.prfx_i, "/" );
300
301       return name_tv.asString() + ret_ti;
302     }
303
304     ///////////////////////////////////////////////////////////////////
305     //
306     //  METHOD NAME : Pathname::Extend
307     //  METHOD TYPE : Pathname
308     //
309     Pathname Pathname::extend( const Pathname & l, const string & r )
310     {
311       return l.asString() + r;
312     }
313
314     /////////////////////////////////////////////////////////////////
315   } // namespace filesystem
316   ///////////////////////////////////////////////////////////////////
317   /////////////////////////////////////////////////////////////////
318 } // namespace zypp
319 ///////////////////////////////////////////////////////////////////