]> git.lyx.org Git - lyx.git/blob - boost/libs/filesystem/src/path_posix_windows.cpp
ccff5bf3c6c87a6046e321ee6d5fef2fa24c19f0
[lyx.git] / boost / libs / filesystem / src / path_posix_windows.cpp
1 //  path implementation  -----------------------------------------------------//
2
3 //  © Copyright Beman Dawes 2002-2003
4 //  Use, modification, and distribution is subject to the Boost Software
5 //  License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
6 //  http://www.boost.org/LICENSE_1_0.txt)
7
8 //  See library home page at http://www.boost.org/libs/filesystem
9
10
11 //****************************************************************************//
12
13 //  WARNING: This code was hacked time and time again as different library 
14 //  designs were tried.  Thus portions may be residue from the earlier
15 //  experiments, and be totally stupid or wrong in light of the final library
16 //  specifications.  The code needs to be reviewed line-by-line to elmininate
17 //  such problems.
18
19 //****************************************************************************//
20
21 // define BOOST_FILESYSTEM_SOURCE so that <boost/filesystem/config.hpp> knows
22 // the library is being built (possibly exporting rather than importing code)
23 #define BOOST_FILESYSTEM_SOURCE 
24
25 // BOOST_POSIX or BOOST_WINDOWS specify which API to use.
26 # if !defined( BOOST_WINDOWS ) && !defined( BOOST_POSIX )
27 #   if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__CYGWIN__)
28 #     define BOOST_WINDOWS
29 #   else
30 #     define BOOST_POSIX
31 #   endif
32 # endif
33
34 #include <boost/filesystem/path.hpp>
35 #include <boost/filesystem/exception.hpp>
36
37 namespace fs = boost::filesystem;
38
39 #include <boost/config.hpp>
40 #ifdef BOOST_NO_STDC_NAMESPACE
41   namespace std { using ::strlen; }
42 #endif
43
44 #include <boost/throw_exception.hpp>
45 #include <cstring>  // SGI MIPSpro compilers need this
46 #include <vector>
47 #include <cassert>
48
49 #include <boost/config/abi_prefix.hpp> // must be the last header
50
51 //  helpers  -----------------------------------------------------------------// 
52
53 namespace
54 {
55   // POSIX & Windows cases: "", "/", "/foo", "foo", "foo/bar"
56   // Windows only cases: "c:", "c:/", "c:foo", "c:/foo",
57   //                     "prn:", "//share", "//share/", "//share/foo"
58
59   std::string::size_type leaf_pos( const std::string & str,
60     std::string::size_type end_pos ) // end_pos is past-the-end position
61   // return 0 if str itself is leaf (or empty) 
62   {
63     if ( end_pos && str[end_pos-1] == '/' ) return end_pos-1;
64     
65     std::string::size_type pos( str.find_last_of( '/', end_pos-1 ) );
66 #   ifdef BOOST_WINDOWS
67     if ( pos == std::string::npos ) pos = str.find_last_of( ':', end_pos-2 );
68 #   endif
69
70     return ( pos == std::string::npos // path itself must be a leaf (or empty)
71 #     ifdef BOOST_WINDOWS
72       || (pos == 1 && str[0] == '/') // or share
73 #     endif
74       ) ? 0 // so leaf is entire string
75         : pos + 1; // or starts after delimiter
76   }
77
78   void first_name( const std::string & src, std::string & target )
79   {
80     target = ""; // VC++ 6.0 doesn't have string::clear()
81     std::string::const_iterator itr( src.begin() );
82
83 #   ifdef BOOST_WINDOWS
84     // deal with //share
85     if ( src.size() >= 2 && src[0] == '/' && src[1] == '/' )
86       { target = "//"; itr += 2; }
87 #   endif
88
89     while ( itr != src.end()
90 #     ifdef BOOST_WINDOWS
91       && *itr != ':'
92 #     endif
93       && *itr != '/' ) { target += *itr++; }
94
95     if ( itr == src.end() ) return;
96
97 #   ifdef BOOST_WINDOWS
98     if ( *itr == ':' )
99     {
100       target += *itr++;
101       return;
102     }
103 #   endif
104
105     // *itr is '/'
106     if ( itr == src.begin() ) { target += '/'; }
107     return;
108   }
109
110   const char invalid_chars[] =
111     "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
112     "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
113     "<>:\"/\\|";
114   // note that the terminating '\0' is part of the string - thus the size below
115   // is sizeof(invalid_chars) rather than sizeof(invalid_chars)-1.  I 
116   const std::string windows_invalid_chars( invalid_chars, sizeof(invalid_chars) );
117
118   const std::string valid_posix(
119     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-" );
120
121   fs::path::name_check default_check = fs::portable_name;
122   bool safe_to_write_check = true; // write-once-before-read allowed
123
124 } // unnamed namespace
125
126 //----------------------------------------------------------------------------// 
127
128 namespace boost
129 {
130   namespace filesystem
131   {
132     //  name_check functions  ----------------------------------------------//
133
134 #   ifdef BOOST_WINDOWS
135     BOOST_FILESYSTEM_DECL bool native( const std::string & name )
136     {
137       return windows_name( name );
138     }
139 #   else
140     BOOST_FILESYSTEM_DECL bool native( const std::string & )
141     {
142       return true;
143     }
144 #   endif
145
146     BOOST_FILESYSTEM_DECL bool no_check( const std::string & ) { return true; }
147
148     BOOST_FILESYSTEM_DECL bool portable_posix_name( const std::string & name )
149     {
150       return name.size() != 0
151         && name.find_first_not_of( valid_posix ) == std::string::npos;     
152     }
153
154     BOOST_FILESYSTEM_DECL bool windows_name( const std::string & name )
155     {
156       return name.size() != 0
157         && name.find_first_of( windows_invalid_chars ) == std::string::npos
158         && *(name.end()-1) != ' '
159         && (*(name.end()-1) != '.'
160           || name.length() == 1 || name == "..");
161     }
162
163     BOOST_FILESYSTEM_DECL bool portable_name( const std::string & name )
164     {
165       return
166         name.size() == 0
167         || name == "."
168         || name == ".."
169         || (windows_name( name )
170         && portable_posix_name( name )
171         && name[0] != '.' && name[0] != '-');
172     }
173
174     BOOST_FILESYSTEM_DECL bool portable_directory_name( const std::string & name )
175     {
176       return
177         name == "."
178         || name == ".."
179         || (portable_name( name )
180           && name.find('.') == std::string::npos);
181     }
182
183     BOOST_FILESYSTEM_DECL bool portable_file_name( const std::string & name )
184     {
185       std::string::size_type pos;
186       return
187          name == "."
188         || name == ".."
189         || (portable_name( name )
190           && ( (pos = name.find( '.' )) == std::string::npos
191             || (name.find( '.', pos+1 )== std::string::npos
192               && (pos + 5) > name.length() )))
193         ;
194     }
195
196
197 //  path implementation  -----------------------------------------------------//
198
199     path::path( const std::string & src )
200     {
201       m_path_append( src, default_name_check() );
202     }
203
204     path::path( const char * src )
205     {
206       assert( src != 0 );
207       m_path_append( src, default_name_check() );
208     }
209
210     path::path( const std::string & src, name_check checker )
211     {
212       m_path_append( src, checker );
213     }
214
215     path::path( const char * src, name_check checker )
216     {
217       assert( src != 0 );
218       m_path_append( src, checker );
219     }
220
221     path & path::operator /=( const path & rhs )
222     {
223       m_path_append( rhs.m_path, no_check );
224       return *this;
225     }
226
227     void path::m_path_append( const std::string & src, name_check checker )
228     {
229       // convert backslash to forward slash if checker==native 
230       // allow system-specific-root if checker==no_check || checker==native
231
232       assert( checker );
233       assert( src.size() == std::strlen( src.c_str() ) ); // no embedded 0
234
235       if ( src.size() == 0 ) return;
236
237       std::string::const_iterator itr( src.begin() );
238
239       // [root-filesystem]
240 #     ifdef BOOST_WINDOWS
241       if ( (checker == no_check || checker == native) && src.size() >= 2 )
242       {
243         // drive or device
244         if ( src[1] == ':' || src[src.size()-1] == ':' )
245         {        
246           for ( ; *itr != ':'; ++itr ) m_path += *itr;
247           m_path += ':';
248           ++itr;
249         }
250
251         // share
252         else if ( (*itr == '/' || (*itr == '\\' && checker == native))
253           && (*(itr+1) == '/' || (*(itr+1) == '\\' && checker == native)) )
254         {
255           m_path += "//";
256           for ( itr += 2;
257                 itr != src.end() && *itr != '/' && *itr != '\\';
258                 ++itr ) m_path += *itr;
259         }
260       }
261 #     endif
262
263       // root directory [ "/" ]
264       if ( itr != src.end() && (*itr == '/'
265 #         ifdef BOOST_WINDOWS
266           || (*itr == '\\' && checker == native)
267 #         endif
268           ) )
269       {
270         ++itr;
271         if ( m_path.size() == 0
272 #         ifdef BOOST_WINDOWS
273           || m_path[m_path.size()-1] == ':' // drive or device
274           || (  // share
275              m_path.size() > 2
276              && m_path[0] == '/'
277              && m_path[1] == '/'
278              && m_path.find( '/', 2 ) == std::string::npos
279              )
280 #         endif
281           ) m_path += '/';
282       }
283
284       // element { "/" element } [ "/" ]
285       while ( itr != src.end() )
286       {
287         if ( m_path == "." ) m_path = "";
288
289         // directory-placeholder
290         if ( *itr == '.' && ((itr+1) == src.end() || *(itr+1) == '/') )
291         {
292           if ( empty() ) m_path += '.';
293           ++itr;
294         }
295
296         // parent-directory or name
297         else
298         {
299           // append '/' if needed
300           if ( !empty()
301               && *(m_path.end()-1) != ':' && *(m_path.end()-1) != '/' )
302               m_path += '/';
303
304           // parent-directory
305           if ( *itr == '.'
306             && (itr+1) != src.end() && *(itr+1) == '.'
307             && ((itr+2) == src.end() || *(itr+2) == '/') )
308           {
309             m_path += "..";
310             ++itr;
311             ++itr;
312           } // parent-directory
313
314           // name
315           else
316           {
317             std::string name;
318             do
319               { name += *itr; }
320             while ( ++itr != src.end() && *itr != '/'
321 #             ifdef BOOST_WINDOWS
322               && (*itr != '\\' || checker != native)
323 #             endif
324               );
325
326             if ( !checker( name ) )
327             {
328               boost::throw_exception( filesystem_error(
329                 "boost::filesystem::path",
330                 "invalid name \"" + name + "\" in path: \"" + src + "\"" ) );
331             }
332
333             m_path += name;
334           }
335         } // parent-directory or name
336
337         // end or "/"
338         if ( itr != src.end() )
339         {
340           if ( *itr == '/'
341 #         ifdef BOOST_WINDOWS
342           || (*itr == '\\' && checker == native)
343 #         endif
344           ) ++itr;
345           else 
346             boost::throw_exception( filesystem_error(
347               "boost::filesystem::path",
348               "invalid path syntax: \"" + src + "\"" ) );
349         }
350
351       } // while more elements
352
353       // special case: remove one or more leading "/.."
354
355       std::string::size_type pos = 0, sz = m_path.size();
356
357 #     ifdef BOOST_WINDOWS
358       if ( sz > 2 && m_path[pos] != '/' && m_path[pos+1] == ':' ) // drive
359         { pos += 2;  sz  -= 2; }
360 #     endif
361
362       while ( sz >= 3 && m_path[pos] == '/'
363          && m_path[pos+1] == '.' && m_path[pos+2] == '.'
364          && (sz == 3 || m_path[pos+3] == '/') )
365       {
366         m_path.erase( pos+1, 3 ); // "..[/]"
367         sz -= 3; // on last, 3 should be 2; that doesn't matter
368       }
369     }
370
371 // path conversion functions  ------------------------------------------------//
372
373     std::string path::native_file_string() const
374     {
375 #   ifdef BOOST_WINDOWS
376       std::string s( m_path );
377       for ( std::string::iterator itr( s.begin() );
378         itr != s.end(); ++itr )
379         if ( *itr == '/' ) *itr = '\\';
380       return s;
381 #   else
382       return m_path;
383 #   endif
384     }
385
386     std::string path::native_directory_string() const
387       { return native_file_string(); }
388
389 // path modification functions -----------------------------------------------//
390
391       path & path::normalize()
392       {
393         if ( m_path.empty() ) return *this;
394         std::string::size_type end, beg(0), start(0);
395
396 #       ifdef BOOST_WINDOWS
397           if ( m_path.size() > 2
398             && m_path[0] != '/' && m_path[1] == ':' ) start = 2; // drive
399 #       endif
400
401         while ( (beg=m_path.find( "/..", beg )) != std::string::npos )
402         {
403           end = beg + 3;
404           if ( (beg == 1 && m_path[0] == '.')
405             || (beg == 2 && m_path[0] == '.' && m_path[1] == '.')
406             || (beg > 2 && m_path[beg-3] == '/'
407                         && m_path[beg-2] == '.' && m_path[beg-1] == '.') )
408           {
409             beg = end;
410             continue;
411           }
412           if ( end < m_path.size() )
413           {
414             if ( m_path[end] == '/' ) ++end;
415             else { beg = end; continue; } // name starts with ..
416           }
417
418           // end is one past end of substr to be erased; now set beg
419           while ( beg > start && m_path[--beg] != '/' ) {}
420           if ( m_path[beg] == '/') ++beg;
421           m_path.erase( beg, end-beg );
422           if ( beg ) --beg;
423         }
424
425         if ( m_path.empty() ) m_path = ".";
426         else
427         { // remove trailing '/' if not root directory
428           std::string::size_type sz = m_path.size();
429
430 #       ifdef BOOST_WINDOWS
431           if ( start ) sz  -= 2; // drive
432 #       endif
433
434           if ( sz > 1 && m_path[m_path.size()-1] == '/' )
435             { m_path.erase( m_path.size()-1 ); }
436         }
437         return *this;
438       }
439
440  // path decomposition functions  ---------------------------------------------//
441
442     path::iterator path::begin() const
443     {
444       iterator itr;
445       itr.m_path_ptr = this;
446       first_name( m_path, itr.m_name );
447       itr.m_pos = 0;
448       return itr;
449     }
450
451     void path::m_replace_leaf( const char * new_leaf )
452     {
453       m_path.erase( leaf_pos( m_path, m_path.size() ) );
454       m_path += new_leaf;
455     }
456
457     std::string path::leaf() const
458     {
459       return m_path.substr( leaf_pos( m_path, m_path.size() ) );
460     }
461
462     namespace detail
463     {
464       inline bool is_absolute_root( const std::string & s,
465         std::string::size_type len )
466       {
467         return
468           len && s[len-1] == '/'
469           &&
470           (
471             len == 1 // "/"
472 #       ifdef BOOST_WINDOWS
473             || ( len > 1
474                  && ( s[len-2] == ':' // drive or device
475                    || ( s[0] == '/'   // share
476                      && s[1] == '/'
477                      && s.find( '/', 2 ) == len-1
478                       )
479                     )
480                 )
481 #       endif
482           );
483       }
484     }
485
486     path path::branch_path() const
487     {
488       std::string::size_type end_pos( leaf_pos( m_path, m_path.size() ) );
489
490       // skip a '/' unless it is a root directory
491       if ( end_pos && m_path[end_pos-1] == '/'
492         && !detail::is_absolute_root( m_path, end_pos ) ) --end_pos;
493       return path( m_path.substr( 0, end_pos ), no_check );
494     }
495
496     path path::relative_path() const
497     {
498       std::string::size_type pos( 0 );
499       if ( m_path.size() && m_path[0] == '/' )
500       { pos = 1;
501 #     ifdef BOOST_WINDOWS
502         if ( m_path.size()>1 && m_path[1] == '/' ) // share
503         {
504           if ( (pos = m_path.find( '/', 2 )) != std::string::npos ) ++pos;
505           else return path();
506         }
507       }
508       else if ( (pos = m_path.find( ':' )) == std::string::npos ) pos = 0;
509       else // has ':'
510       {
511         if ( ++pos < m_path.size() && m_path[pos] == '/' ) ++pos;
512 #     endif
513       }
514       return path( m_path.substr( pos ), no_check );
515     }
516
517     std::string path::root_name() const
518     {
519 #   ifdef BOOST_WINDOWS
520       std::string::size_type pos( m_path.find( ':' ) );
521       if ( pos != std::string::npos ) return m_path.substr( 0, pos+1 );
522       if ( m_path.size() > 2 && m_path[0] == '/' && m_path[1] == '/' )
523       {
524         pos = m_path.find( '/', 2 );
525         return m_path.substr( 0, pos );
526       }
527 #   endif
528       return std::string();
529     }
530
531     std::string path::root_directory() const
532     {
533       return std::string(
534         ( m_path.size() && m_path[0] == '/' )  // covers both "/" and "//share"
535 #       ifdef BOOST_WINDOWS
536           || ( m_path.size() > 2
537                && m_path[1] == ':'
538                && m_path[2] == '/' )  // "c:/"
539 #       endif
540                ? "/" : "" );
541     }
542
543     path path::root_path() const
544     {
545       return path(
546 #   ifdef BOOST_WINDOWS
547         root_name(), no_check ) /= root_directory();
548 #   else
549         root_directory() );
550 #   endif
551     }
552
553 // path query functions  -----------------------------------------------------//
554
555     bool path::is_complete() const
556     {
557 #   ifdef BOOST_WINDOWS
558       return m_path.size() > 2
559         && ( (m_path[1] == ':' && m_path[2] == '/') // "c:/"
560           || (m_path[0] == '/' && m_path[1] == '/') // "//share"
561           || m_path[m_path.size()-1] == ':' );
562 #   else
563       return m_path.size() && m_path[0] == '/';
564 #   endif
565     }
566
567     bool path::has_root_path() const
568     {
569       return ( m_path.size() 
570                && m_path[0] == '/' )  // covers both "/" and "//share"
571 #            ifdef BOOST_WINDOWS
572                || ( m_path.size() > 1 && m_path[1] == ':' ) // "c:" and "c:/"
573                || ( m_path.size() > 3
574                     && m_path[m_path.size()-1] == ':' ) // "device:"
575 #            endif
576                ;
577     }
578
579     bool path::has_root_name() const
580     {
581 #   ifdef BOOST_WINDOWS
582       return m_path.size() > 1
583         && ( m_path[1] == ':' // "c:"
584           || m_path[m_path.size()-1] == ':' // "prn:"
585           || (m_path[0] == '/' && m_path[1] == '/') // "//share"
586            );
587 #   else
588       return false;
589 #   endif
590     }
591
592     bool path::has_root_directory() const
593     {
594       return ( m_path.size() 
595                && m_path[0] == '/' )  // covers both "/" and "//share"
596 #            ifdef BOOST_WINDOWS
597                || ( m_path.size() > 2
598                     && m_path[1] == ':' && m_path[2] == '/' ) // "c:/"
599 #            endif
600                ;
601     }
602
603     bool path::has_relative_path() const { return !relative_path().empty(); }
604     bool path::has_branch_path() const { return !branch_path().empty(); }
605
606     //  default name_check mechanism  ----------------------------------------//
607
608     bool path::default_name_check_writable()
609     {
610       return safe_to_write_check;
611     }
612
613     void path::default_name_check( name_check new_check )
614     {
615       assert( new_check );
616       if ( !safe_to_write_check )
617         boost::throw_exception(
618           filesystem_error( "boost::filesystem::default_name_check",
619                             "default name check already set" ));
620       default_check = new_check;
621       safe_to_write_check = false;
622     }
623
624     path::name_check path::default_name_check()
625     {
626       safe_to_write_check = false;
627       return default_check;
628     }
629
630 // path::iterator implementation  --------------------------------------------// 
631
632     void path::iterator::increment()
633     {
634       assert( m_pos < m_path_ptr->m_path.size() ); // detect increment past end
635       m_pos += m_name.size();
636       if ( m_pos == m_path_ptr->m_path.size() )
637       {
638         m_name = "";  // not strictly required, but might aid debugging
639         return;
640       }
641       if ( m_path_ptr->m_path[m_pos] == '/' )
642       {
643 #       ifdef BOOST_WINDOWS
644         if ( m_name[m_name.size()-1] == ':' // drive or device
645           || (m_name[0] == '/' && m_name[1] == '/') ) // share
646         {
647           m_name = "/";
648           return;
649         }
650 #       endif
651         ++m_pos;
652       }
653       std::string::size_type end_pos( m_path_ptr->m_path.find( '/', m_pos ) );
654       if ( end_pos == std::string::npos )
655         end_pos = m_path_ptr->m_path.size();
656       m_name = m_path_ptr->m_path.substr( m_pos, end_pos - m_pos );
657     }
658
659     void path::iterator::decrement()
660     {                                                                                
661       assert( m_pos ); // detect decrement of begin
662       std::string::size_type end_pos( m_pos );
663
664       // skip a '/' unless it is a root directory
665       if ( m_path_ptr->m_path[end_pos-1] == '/'
666         && !detail::is_absolute_root( m_path_ptr->m_path, end_pos ) ) --end_pos;
667       m_pos = leaf_pos( m_path_ptr->m_path, end_pos );
668       m_name = m_path_ptr->m_path.substr( m_pos, end_pos - m_pos );
669     }
670   } // namespace filesystem
671 } // namespace boost