]> git.lyx.org Git - features.git/blob - boost/libs/filesystem/src/path_posix_windows.cpp
boost::filesystem added
[features.git] / boost / libs / filesystem / src / path_posix_windows.cpp
1 //  path implementation  -----------------------------------------------------//
2
3 //  (C) Copyright Beman Dawes 2002. Permission to copy,
4 //  use, modify, sell and distribute this software is granted provided this
5 //  copyright notice appears in all copies. This software is provided "as is"
6 //  without express or implied warranty, and with no claim as to its
7 //  suitability for any purpose.
8
9 //  See http://www.boost.org/libs/filesystem for documentation.
10
11
12 //****************************************************************************//
13
14 //  WARNING: This code was hacked time and time again as different library 
15 //  designs were tried.  Thus portions may be residue from the earlier
16 //  experiments, and be totally stupid or wrong in light of the final library
17 //  specifications.  The code needs to be reviewed line-by-line to elmininate
18 //  such problems.
19
20 //****************************************************************************//
21
22 // BOOST_POSIX or BOOST_WINDOWS specify which API to use.
23 # if !defined( BOOST_WINDOWS ) && !defined( BOOST_POSIX )
24 #   if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__CYGWIN__)
25 #     define BOOST_WINDOWS
26 #   else
27 #     define BOOST_POSIX
28 #   endif
29 # endif
30
31 #include <boost/filesystem/path.hpp>
32 #include <boost/filesystem/exception.hpp>
33
34 namespace fs = boost::filesystem;
35
36 #include <boost/config.hpp>
37 #ifdef BOOST_NO_STDC_NAMESPACE
38   namespace std { using ::strlen; }
39 #endif
40
41 #include <boost/throw_exception.hpp>
42 #include <cstring>  // SGI MIPSpro compilers need this
43 #include <vector>
44 #include <cassert>
45
46 //  helpers  -----------------------------------------------------------------// 
47
48 namespace
49 {
50   // POSIX & Windows cases: "", "/", "/foo", "foo", "foo/bar"
51   // Windows only cases: "c:", "c:/", "c:foo", "c:/foo",
52   //                     "prn:", "//share", "//share/", "//share/foo"
53
54   std::string::size_type leaf_pos( const std::string & str,
55     std::string::size_type end_pos ) // end_pos is past-the-end position
56   // return 0 if str itself is leaf (or empty) 
57   {
58     if ( end_pos && str[end_pos-1] == '/' ) return end_pos-1;
59     
60     std::string::size_type pos( str.find_last_of( '/', end_pos-1 ) );
61 #   ifdef BOOST_WINDOWS
62     if ( pos == std::string::npos ) pos = str.find_last_of( ':', end_pos-2 );
63 #   endif
64
65     return ( pos == std::string::npos // path itself must be a leaf (or empty)
66 #     ifdef BOOST_WINDOWS
67       || (pos == 1 && str[0] == '/') // or share
68 #     endif
69       ) ? 0 // so leaf is entire string
70         : pos + 1; // or starts after delimiter
71   }
72
73   void first_name( const std::string & src, std::string & target )
74   {
75     target = ""; // VC++ 6.0 doesn't have string::clear()
76     std::string::const_iterator itr( src.begin() );
77
78 #   ifdef BOOST_WINDOWS
79     // deal with //share
80     if ( src.size() >= 2 && src[0] == '/' && src[1] == '/' )
81       { target = "//"; itr += 2; }
82 #   endif
83
84     while ( itr != src.end()
85 #     ifdef BOOST_WINDOWS
86       && *itr != ':'
87 #     endif
88       && *itr != '/' ) { target += *itr++; }
89
90     if ( itr == src.end() ) return;
91
92 #   ifdef BOOST_WINDOWS
93     if ( *itr == ':' )
94     {
95       target += *itr++;
96       return;
97     }
98 #   endif
99
100     // *itr is '/'
101     if ( itr == src.begin() ) { target += '/'; }
102     return;
103   }
104
105   const char invalid_chars[] =
106     "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
107     "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
108     "<>:\"/\\|*?";
109   // note that the terminating '\0' is part of the string - thus the size below
110   // is sizeof(invalid_chars) rather than sizeof(invalid_chars)-1.  I 
111   const std::string invalid_generic( invalid_chars, sizeof(invalid_chars) );
112
113   const std::string valid_posix(
114     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-" );
115
116   const std::string valid_boost_file(
117     "abcdefghijklmnopqrstuvwxyz0123456789._-" );
118   const std::string valid_boost_directory(
119     "abcdefghijklmnopqrstuvwxyz0123456789_-" );
120
121 } // unnamed namespace
122
123 //----------------------------------------------------------------------------// 
124
125 namespace boost
126 {
127   namespace filesystem
128   {
129
130     //  error checking functions  --------------------------------------------//
131
132     bool generic_name( const std::string & name )
133     {
134       return name.size() != 0
135         && name.find_first_of( invalid_generic ) == std::string::npos
136         && name != "."
137         && name != ".."
138         && *name.begin() != ' '
139         && *(name.end()-1) != ' ';     
140     }
141
142     bool posix_name( const std::string & name )
143     {
144       return name.find_first_not_of( valid_posix ) == std::string::npos
145         && name != ".";     
146     }
147
148     const path & check_posix_leaf( const path & ph )
149     {
150       if ( !posix_name( ph.leaf() ) )
151         boost::throw_exception( filesystem_error(
152           "boost::filesystem::check_posix_leaf",
153           ph, "invalid posix name: \""
154             + ph.leaf() + "\"" ) );
155       return ph;
156     }
157
158     bool boost_file_name( const std::string & name )
159     {
160       return name.size() <= 31
161         && name.find_first_not_of( valid_boost_file ) == std::string::npos
162         && name != ".";     
163     }
164
165     bool boost_directory_name( const std::string & name )
166     {
167       return name.size() <= 31
168         && name.find_first_not_of( valid_boost_directory ) == std::string::npos;     
169     }
170
171 //  path implementation  -----------------------------------------------------//
172
173     path::path( const std::string & src )
174     {
175       m_path_append( src );
176     }
177
178     path::path( const char * src )
179     {
180       m_path_append( src );
181     }
182
183     path::path( const std::string & src, path_format )
184     {
185       m_path_append( src, platform );
186     }
187
188     path::path( const char * src, path_format )
189     {
190       m_path_append( src, platform );
191     }
192
193     path & path::operator /=( const path & rhs )
194     {
195       m_path_append( rhs.m_path, nocheck );
196       return *this;
197     }
198
199     void path::m_path_append( const std::string & src, source_context context )
200     {
201       // convert backslash to forward slash if context is "platform" 
202       // check names if context is "generic"
203       // allow system-specific-root if context is not "generic"
204
205       assert( src.size() == std::strlen( src.c_str() ) ); // no embedded 0
206
207       if ( src.size() == 0 ) return;
208
209       std::string::const_iterator itr( src.begin() );
210
211       // [root-filesystem]
212 #     ifdef BOOST_WINDOWS
213       if ( context != generic && src.size() >= 2 )
214       {
215         // drive or device
216         if ( src[1] == ':' || src[src.size()-1] == ':' )
217         {        
218           for ( ; *itr != ':'; ++itr ) m_path += *itr;
219           m_path += ':';
220           ++itr;
221         }
222
223         // share
224         else if ( (*itr == '/' || (*itr == '\\' && context == platform))
225           && (*(itr+1) == '/' || (*(itr+1) == '\\' && context == platform)) )
226         {
227           m_path += "//";
228           for ( itr += 2;
229                 itr != src.end() && *itr != '/' && *itr != '\\';
230                 ++itr ) m_path += *itr;
231         }
232       }
233 #     endif
234
235       // root directory [ "/" ]
236       if ( itr != src.end() && (*itr == '/'
237 #         ifdef BOOST_WINDOWS
238           || (*itr == '\\' && context == platform)
239 #         endif
240           ) )
241       {
242         ++itr;
243         if ( m_path.size() == 0
244 #         ifdef BOOST_WINDOWS
245           || m_path[m_path.size()-1] == ':' // drive or device
246           || (  // share
247              m_path.size() > 2
248              && m_path[0] == '/'
249              && m_path[1] == '/'
250              && m_path.find( '/', 2 ) == std::string::npos
251              )
252 #         endif
253           ) m_path += '/';
254       }
255
256       // element { "/" element } [ "/" ]
257       while ( itr != src.end() )
258       {
259
260         // append '/' if needed
261         if ( !empty()
262             && *(m_path.end()-1) != ':' && *(m_path.end()-1) != '/' )
263             m_path += '/'; 
264
265         if ( *itr == '.'&& (itr+1) != src.end() && *(itr+1) == '.' ) // ".."
266         {
267           if ( m_path.size() >= 2 // there is a named parent directory present
268             && *(m_path.end()-1) == '/'
269 #           ifdef BOOST_WINDOWS
270             && *(m_path.end()-2) != ':'
271 #           endif
272             && *(m_path.end()-2) != '.' )    
273           {
274             // reference to parent so erase child
275             std::string::iterator child( m_path.end()-2 );
276
277             while ( child != m_path.begin() && *child != '/'
278 #             ifdef BOOST_WINDOWS
279               && *child != ':'
280 #             endif
281               ) --child;
282
283             // only erase '/' if it is a separator rather than part of the root
284             if ( (*child == '/'
285               && (child == m_path.begin()
286 #             ifdef BOOST_WINDOWS
287                 || *(child-1) == ':'))
288               || *child == ':'
289 #             else                           
290               ))         
291 #             endif              
292               ) ++child;
293
294             m_path.erase( child, m_path.end() );
295           }
296           else { m_path += ".."; }
297           ++itr;
298           ++itr;
299         } // ".."
300         else // element is name
301         {
302           std::string name;
303           do
304             { name += *itr; }
305           while ( ++itr != src.end() && *itr != '/'
306 #           ifdef BOOST_WINDOWS
307             && (*itr != '\\' || context != platform)
308 #           endif
309             );
310
311           if ( context == generic && !generic_name( name ) )
312           {
313             boost::throw_exception( filesystem_error(
314               "boost::filesystem::path",
315               "invalid name \"" + name + "\" in path: \"" + src + "\"" ) );
316           }
317
318           m_path += name;
319         }
320
321         if ( itr != src.end() )
322         {
323           if ( *itr == '/'
324 #         ifdef BOOST_WINDOWS
325           || (*itr == '\\' && context == platform)
326 #         endif
327           ) ++itr;
328           else 
329             boost::throw_exception( filesystem_error(
330               "boost::filesystem::path",
331               "invalid path syntax: \"" + src + "\"" ) );
332         }
333
334       } // while more elements
335     }
336
337 // path conversion functions  ------------------------------------------------//
338
339     std::string path::native_file_string() const
340     {
341 #   ifdef BOOST_WINDOWS
342       std::string s( m_path );
343       for ( std::string::iterator itr( s.begin() );
344         itr != s.end(); ++itr )
345         if ( *itr == '/' ) *itr = '\\';
346       return s;
347 #   else
348       return m_path;
349 #   endif
350     }
351
352     std::string path::native_directory_string() const
353       { return native_file_string(); }
354
355 // path decomposition functions  ---------------------------------------------//
356
357     path::iterator path::begin() const
358     {
359       iterator itr;
360       itr.base().path_ptr = this;
361       first_name( m_path, itr.base().name );
362       itr.base().pos = 0;
363       return itr;
364     }
365
366     void path::m_replace_leaf( const char * new_leaf )
367     {
368       m_path.erase( leaf_pos( m_path, m_path.size() ) );
369       m_path += new_leaf;
370     }
371
372     std::string path::leaf() const
373     {
374       return m_path.substr( leaf_pos( m_path, m_path.size() ) );
375     }
376
377     namespace detail
378     {
379       inline bool is_absolute_root( const std::string & s,
380         std::string::size_type len )
381       {
382         return
383           len && s[len-1] == '/'
384           &&
385           (
386             len == 1 // "/"
387 #       ifdef BOOST_WINDOWS
388             || ( len > 1
389                  && ( s[len-2] == ':' // drive or device
390                    || ( s[0] == '/'   // share
391                      && s[1] == '/'
392                      && s.find( '/', 2 ) == len-1
393                       )
394                     )
395                 )
396 #       endif
397           );
398       }
399     }
400
401     path path::branch_path() const
402     {
403       std::string::size_type end_pos( leaf_pos( m_path, m_path.size() ) );
404
405       // skip a '/' unless it is a root directory
406       if ( end_pos && m_path[end_pos-1] == '/'
407         && !detail::is_absolute_root( m_path, end_pos ) ) --end_pos;
408       return path( m_path.substr( 0, end_pos ), native );
409     }
410
411     path path::relative_path() const
412     {
413       std::string::size_type pos( 0 );
414       if ( m_path.size() && m_path[0] == '/' )
415       { pos = 1;
416 #     ifdef BOOST_WINDOWS
417         if ( m_path.size()>1 && m_path[1] == '/' ) // share
418         {
419           if ( (pos = m_path.find( '/', 2 )) != std::string::npos ) ++pos;
420           else return path();
421         }
422       }
423       else if ( (pos = m_path.find( ':' )) == std::string::npos ) pos = 0;
424       else // has ':'
425       {
426         if ( ++pos < m_path.size() && m_path[pos] == '/' ) ++pos;
427 #     endif
428       }
429       return path( m_path.substr( pos ) );
430     }
431
432     std::string path::root_name() const
433     {
434 #   ifdef BOOST_WINDOWS
435       std::string::size_type pos( m_path.find( ':' ) );
436       if ( pos != std::string::npos ) return m_path.substr( 0, pos+1 );
437       if ( m_path.size() > 2 && m_path[0] == '/' && m_path[1] == '/' )
438       {
439         pos = m_path.find( '/', 2 );
440         return m_path.substr( 0, pos );
441       }
442 #   endif
443       return std::string();
444     }
445
446     std::string path::root_directory() const
447     {
448       return std::string(
449         ( m_path.size() && m_path[0] == '/' )  // covers both "/" and "//share"
450 #       ifdef BOOST_WINDOWS
451           || ( m_path.size() > 2
452                && m_path[1] == ':'
453                && m_path[2] == '/' )  // "c:/"
454 #       endif
455                ? "/" : "" );
456     }
457
458     path path::root_path() const
459     {
460       return path(
461 #   ifdef BOOST_WINDOWS
462         root_name(), native ) /= root_directory();
463 #   else
464         root_directory() );
465 #   endif
466     }
467
468 // path query functions  -----------------------------------------------------//
469
470     bool path::is_complete() const
471     {
472 #   ifdef BOOST_WINDOWS
473       return m_path.size() > 2
474         && ( (m_path[1] == ':' && m_path[2] == '/') // "c:/"
475           || (m_path[0] == '/' && m_path[1] == '/') // "//share"
476           || m_path[m_path.size()-1] == ':' );
477 #   else
478       return m_path.size() && m_path[0] == '/';
479 #   endif
480     }
481
482     bool path::has_root_path() const
483     {
484       return ( m_path.size() 
485                && m_path[0] == '/' )  // covers both "/" and "//share"
486 #            ifdef BOOST_WINDOWS
487                || ( m_path.size() > 1 && m_path[1] == ':' ) // "c:" and "c:/"
488                || ( m_path.size() > 3
489                     && m_path[m_path.size()-1] == ':' ) // "device:"
490 #            endif
491                ;
492     }
493
494     bool path::has_root_name() const
495     {
496 #   ifdef BOOST_WINDOWS
497       return m_path.size() > 1
498         && ( m_path[1] == ':' // "c:"
499           || m_path[m_path.size()-1] == ':' // "prn:"
500           || (m_path[0] == '/' && m_path[1] == '/') // "//share"
501            );
502 #   else
503       return false;
504 #   endif
505     }
506
507     bool path::has_root_directory() const
508     {
509       return ( m_path.size() 
510                && m_path[0] == '/' )  // covers both "/" and "//share"
511 #            ifdef BOOST_WINDOWS
512                || ( m_path.size() > 2
513                     && m_path[1] == ':' && m_path[2] == '/' ) // "c:/"
514 #            endif
515                ;
516     }
517
518     bool path::has_relative_path() const { return !relative_path().empty(); }
519     bool path::has_branch_path() const { return !branch_path().empty(); }
520
521
522 // path_itr_imp implementation  ----------------------------------------------// 
523
524     namespace detail
525     {
526       void path_itr_imp::operator++()
527       {
528         assert( pos < path_ptr->m_path.size() ); // detect increment past end
529         pos += name.size();
530         if ( pos == path_ptr->m_path.size() )
531         {
532           name = "";  // not strictly required, but might aid debugging
533           return;
534         }
535         if ( path_ptr->m_path[pos] == '/' )
536         {
537 #       ifdef BOOST_WINDOWS
538           if ( name[name.size()-1] == ':' // drive or device
539             || (name[0] == '/' && name[1] == '/') ) // share
540           {
541             name = "/";
542             return;
543           }
544 #       endif
545           ++pos;
546         }
547         std::string::size_type end_pos( path_ptr->m_path.find( '/', pos ) );
548         if ( end_pos == std::string::npos ) end_pos = path_ptr->m_path.size();
549         name = path_ptr->m_path.substr( pos, end_pos - pos );
550       }
551
552       void path_itr_imp::operator--()
553       {                                                                                
554         assert( pos ); // detect decrement of begin
555         std::string::size_type end_pos( pos );
556
557         // skip a '/' unless it is a root directory
558         if ( path_ptr->m_path[end_pos-1] == '/'
559           && !detail::is_absolute_root( path_ptr->m_path, end_pos ) ) --end_pos;
560         pos = leaf_pos( path_ptr->m_path, end_pos );
561         name = path_ptr->m_path.substr( pos, end_pos - pos );
562       }
563
564     } // namespace detail
565   } // namespace filesystem
566 } // namespace boost