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