Imported Upstream version 1.57.0
[platform/upstream/boost.git] / libs / filesystem / src / operations.cpp
1 //  operations.cpp  --------------------------------------------------------------------//
2
3 //  Copyright 2002-2009, 2014 Beman Dawes
4 //  Copyright 2001 Dietmar Kuehl
5
6 //  Distributed under the Boost Software License, Version 1.0.
7 //  See http://www.boost.org/LICENSE_1_0.txt
8
9 //  See library home page at http://www.boost.org/libs/filesystem
10
11 //--------------------------------------------------------------------------------------// 
12
13 //  define 64-bit offset macros BEFORE including boost/config.hpp (see ticket #5355) 
14 #if !(defined(__HP_aCC) && defined(_ILP32) && !defined(_STATVFS_ACPP_PROBLEMS_FIXED))
15 #define _FILE_OFFSET_BITS 64 // at worst, these defines may have no effect,
16 #endif
17 #if !defined(__PGI)
18 #define __USE_FILE_OFFSET64 // but that is harmless on Windows and on POSIX
19       // 64-bit systems or on 32-bit systems which don't have files larger 
20       // than can be represented by a traditional POSIX/UNIX off_t type. 
21       // OTOH, defining them should kick in 64-bit off_t's (and thus 
22       // st_size)on 32-bit systems that provide the Large File
23       // Support (LFS)interface, such as Linux, Solaris, and IRIX.
24       // The defines are given before any headers are included to
25       // ensure that they are available to all included headers.
26       // That is required at least on Solaris, and possibly on other
27       // systems as well.
28 #else
29 #define _FILE_OFFSET_BITS 64
30 #endif
31
32 // define BOOST_FILESYSTEM_SOURCE so that <boost/filesystem/config.hpp> knows
33 // the library is being built (possibly exporting rather than importing code)
34 #define BOOST_FILESYSTEM_SOURCE 
35
36 #ifndef BOOST_SYSTEM_NO_DEPRECATED 
37 # define BOOST_SYSTEM_NO_DEPRECATED
38 #endif
39
40 #ifndef _POSIX_PTHREAD_SEMANTICS
41 # define _POSIX_PTHREAD_SEMANTICS  // Sun readdir_r()needs this
42 #endif
43
44 #include <boost/filesystem/operations.hpp>
45 #include <boost/scoped_array.hpp>
46 #include <boost/detail/workaround.hpp>
47 #include <vector> 
48 #include <cstdlib>     // for malloc, free
49 #include <cstring>
50 #include <cstdio>      // for remove, rename
51 #if defined(__QNXNTO__)  // see ticket #5355 
52 # include <stdio.h>
53 #endif
54 #include <cerrno>
55
56 #ifdef BOOST_FILEYSTEM_INCLUDE_IOSTREAM
57 # include <iostream>
58 #endif
59
60 namespace fs = boost::filesystem;
61 using boost::filesystem::path;
62 using boost::filesystem::filesystem_error;
63 using boost::filesystem::perms;
64 using boost::system::error_code;
65 using boost::system::error_category;
66 using boost::system::system_category;
67 using std::string;
68 using std::wstring;
69
70 # ifdef BOOST_POSIX_API
71
72 #   include <sys/types.h>
73 #   include <sys/stat.h>
74 #   if !defined(__APPLE__) && !defined(__OpenBSD__) && !defined(__ANDROID__)
75 #     include <sys/statvfs.h>
76 #     define BOOST_STATVFS statvfs
77 #     define BOOST_STATVFS_F_FRSIZE vfs.f_frsize
78 #   else
79 #     ifdef __OpenBSD__
80 #     include <sys/param.h>
81 #     elif defined(__ANDROID__)
82 #     include <sys/vfs.h>
83 #     endif
84 #     include <sys/mount.h>
85 #     define BOOST_STATVFS statfs
86 #     define BOOST_STATVFS_F_FRSIZE static_cast<boost::uintmax_t>(vfs.f_bsize)
87 #   endif
88 #   include <dirent.h>
89 #   include <unistd.h>
90 #   include <fcntl.h>
91 #   include <utime.h>
92 #   include "limits.h"
93
94 # else // BOOST_WINDOW_API
95
96 #   if (defined(__MINGW32__) || defined(__CYGWIN__)) && !defined(WINVER)
97       // Versions of MinGW or Cygwin that support Filesystem V3 support at least WINVER 0x501.
98       // See MinGW's windef.h
99 #     define WINVER 0x501
100 #   endif
101 #   include <io.h>
102 #   include <windows.h>
103 #   include <winnt.h>
104 #   if !defined(_WIN32_WINNT)
105 #     define  _WIN32_WINNT   0x0500
106 #   endif
107 #   if defined(__BORLANDC__) || defined(__MWERKS__)
108 #     if defined(__BORLANDC__)
109         using std::time_t;
110 #     endif
111 #     include <utime.h>
112 #   else
113 #     include <sys/utime.h>
114 #   endif
115
116 //  REPARSE_DATA_BUFFER related definitions are found in ntifs.h, which is part of the 
117 //  Windows Device Driver Kit. Since that's inconvenient, the definitions are provided
118 //  here. See http://msdn.microsoft.com/en-us/library/ms791514.aspx
119
120 #if !defined(REPARSE_DATA_BUFFER_HEADER_SIZE)  // mingw winnt.h does provide the defs
121
122 #define SYMLINK_FLAG_RELATIVE 1
123
124 typedef struct _REPARSE_DATA_BUFFER {
125   ULONG  ReparseTag;
126   USHORT  ReparseDataLength;
127   USHORT  Reserved;
128   union {
129     struct {
130       USHORT  SubstituteNameOffset;
131       USHORT  SubstituteNameLength;
132       USHORT  PrintNameOffset;
133       USHORT  PrintNameLength;
134       ULONG  Flags;
135       WCHAR  PathBuffer[1];
136   /*  Example of distinction between substitute and print names:
137         mklink /d ldrive c:\
138         SubstituteName: c:\\??\
139         PrintName: c:\
140   */
141      } SymbolicLinkReparseBuffer;
142     struct {
143       USHORT  SubstituteNameOffset;
144       USHORT  SubstituteNameLength;
145       USHORT  PrintNameOffset;
146       USHORT  PrintNameLength;
147       WCHAR  PathBuffer[1];
148       } MountPointReparseBuffer;
149     struct {
150       UCHAR  DataBuffer[1];
151     } GenericReparseBuffer;
152   };
153 } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
154
155 #define REPARSE_DATA_BUFFER_HEADER_SIZE \
156   FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)
157
158 #endif
159
160 #ifndef MAXIMUM_REPARSE_DATA_BUFFER_SIZE
161 #define MAXIMUM_REPARSE_DATA_BUFFER_SIZE  ( 16 * 1024 )
162 #endif
163
164 # ifndef FSCTL_GET_REPARSE_POINT
165 #   define FSCTL_GET_REPARSE_POINT 0x900a8
166 # endif
167
168 # ifndef IO_REPARSE_TAG_SYMLINK
169 #   define IO_REPARSE_TAG_SYMLINK (0xA000000CL)       
170 # endif
171
172 # endif  // BOOST_WINDOWS_API
173
174 //  BOOST_FILESYSTEM_STATUS_CACHE enables file_status cache in
175 //  dir_itr_increment. The config tests are placed here because some of the
176 //  macros being tested come from dirent.h.
177 //
178 // TODO: find out what macros indicate dirent::d_type present in more libraries
179 # if defined(BOOST_WINDOWS_API)\
180   || defined(_DIRENT_HAVE_D_TYPE)// defined by GNU C library if d_type present
181 #   define BOOST_FILESYSTEM_STATUS_CACHE
182 # endif
183
184 //  POSIX/Windows macros  ----------------------------------------------------//
185
186 //  Portions of the POSIX and Windows API's are very similar, except for name,
187 //  order of arguments, and meaning of zero/non-zero returns. The macros below
188 //  abstract away those differences. They follow Windows naming and order of
189 //  arguments, and return true to indicate no error occurred. [POSIX naming,
190 //  order of arguments, and meaning of return were followed initially, but
191 //  found to be less clear and cause more coding errors.]
192
193 # if defined(BOOST_POSIX_API)
194
195 //  POSIX uses a 0 return to indicate success
196 #   define BOOST_ERRNO    errno 
197 #   define BOOST_SET_CURRENT_DIRECTORY(P)(::chdir(P)== 0)
198 #   define BOOST_CREATE_DIRECTORY(P)(::mkdir(P, S_IRWXU|S_IRWXG|S_IRWXO)== 0)
199 #   define BOOST_CREATE_HARD_LINK(F,T)(::link(T, F)== 0)
200 #   define BOOST_CREATE_SYMBOLIC_LINK(F,T,Flag)(::symlink(T, F)== 0)
201 #   define BOOST_REMOVE_DIRECTORY(P)(::rmdir(P)== 0)
202 #   define BOOST_DELETE_FILE(P)(::unlink(P)== 0)
203 #   define BOOST_COPY_DIRECTORY(F,T)(!(::stat(from.c_str(), &from_stat)!= 0\
204          || ::mkdir(to.c_str(),from_stat.st_mode)!= 0))
205 #   define BOOST_COPY_FILE(F,T,FailIfExistsBool)copy_file_api(F, T, FailIfExistsBool)
206 #   define BOOST_MOVE_FILE(OLD,NEW)(::rename(OLD, NEW)== 0)
207 #   define BOOST_RESIZE_FILE(P,SZ)(::truncate(P, SZ)== 0)
208
209 #   define BOOST_ERROR_NOT_SUPPORTED ENOSYS
210 #   define BOOST_ERROR_ALREADY_EXISTS EEXIST
211
212 # else  // BOOST_WINDOWS_API
213
214 //  Windows uses a non-0 return to indicate success
215 #   define BOOST_ERRNO    ::GetLastError()
216 #   define BOOST_SET_CURRENT_DIRECTORY(P)(::SetCurrentDirectoryW(P)!= 0)
217 #   define BOOST_CREATE_DIRECTORY(P)(::CreateDirectoryW(P, 0)!= 0)
218 #   define BOOST_CREATE_HARD_LINK(F,T)(create_hard_link_api(F, T, 0)!= 0)
219 #   define BOOST_CREATE_SYMBOLIC_LINK(F,T,Flag)(create_symbolic_link_api(F, T, Flag)!= 0)
220 #   define BOOST_REMOVE_DIRECTORY(P)(::RemoveDirectoryW(P)!= 0)
221 #   define BOOST_DELETE_FILE(P)(::DeleteFileW(P)!= 0)
222 #   define BOOST_COPY_DIRECTORY(F,T)(::CreateDirectoryExW(F, T, 0)!= 0)
223 #   define BOOST_COPY_FILE(F,T,FailIfExistsBool)(::CopyFileW(F, T, FailIfExistsBool)!= 0)
224 #   define BOOST_MOVE_FILE(OLD,NEW)(::MoveFileExW(OLD, NEW, MOVEFILE_REPLACE_EXISTING|MOVEFILE_COPY_ALLOWED)!= 0)
225 #   define BOOST_RESIZE_FILE(P,SZ)(resize_file_api(P, SZ)!= 0)
226 #   define BOOST_READ_SYMLINK(P,T)
227
228 #   define BOOST_ERROR_ALREADY_EXISTS ERROR_ALREADY_EXISTS
229 #   define BOOST_ERROR_NOT_SUPPORTED ERROR_NOT_SUPPORTED
230
231 # endif
232
233 //--------------------------------------------------------------------------------------//
234 //                                                                                      //
235 //                        helpers (all operating systems)                              //
236 //                                                                                      //
237 //--------------------------------------------------------------------------------------//
238
239 namespace
240 {
241
242   fs::file_type query_file_type(const path& p, error_code* ec);
243
244   boost::filesystem::directory_iterator end_dir_itr;
245
246   const error_code ok;
247
248   bool error(bool was_error, error_code* ec, const string& message)
249   {
250     if (!was_error)
251     {
252       if (ec != 0) ec->clear();
253     }
254     else  
255     { //  error
256       if (ec == 0)
257         BOOST_FILESYSTEM_THROW(filesystem_error(message,
258           error_code(BOOST_ERRNO, system_category())));
259       else
260         ec->assign(BOOST_ERRNO, system_category());
261     }
262     return was_error;
263   }
264
265   bool error(bool was_error, const path& p, error_code* ec, const string& message)
266   {
267     if (!was_error)
268     {
269       if (ec != 0) ec->clear();
270     }
271     else  
272     { //  error
273       if (ec == 0)
274         BOOST_FILESYSTEM_THROW(filesystem_error(message,
275           p, error_code(BOOST_ERRNO, system_category())));
276       else
277         ec->assign(BOOST_ERRNO, system_category());
278     }
279     return was_error;
280   }
281
282   bool error(bool was_error, const path& p1, const path& p2, error_code* ec,
283     const string& message)
284   {
285     if (!was_error)
286     {
287       if (ec != 0) ec->clear();
288     }
289     else  
290     { //  error
291       if (ec == 0)
292         BOOST_FILESYSTEM_THROW(filesystem_error(message,
293           p1, p2, error_code(BOOST_ERRNO, system_category())));
294       else
295         ec->assign(BOOST_ERRNO, system_category());
296     }
297     return was_error;
298   }
299
300   bool error(bool was_error, const error_code& result,
301     const path& p, error_code* ec, const string& message)
302     //  Overwrites ec if there has already been an error
303   {
304     if (!was_error)
305     {
306       if (ec != 0) ec->clear();
307     }
308     else  
309     { //  error
310       if (ec == 0)
311         BOOST_FILESYSTEM_THROW(filesystem_error(message, p, result));
312       else
313         *ec = result;
314     }
315     return was_error;
316   }
317
318   bool error(bool was_error, const error_code& result,
319     const path& p1, const path& p2, error_code* ec, const string& message)
320     //  Overwrites ec if there has already been an error
321   {
322     if (!was_error)
323     {
324       if (ec != 0) ec->clear();
325     }
326     else  
327     { //  error
328       if (ec == 0)
329         BOOST_FILESYSTEM_THROW(filesystem_error(message, p1, p2, result));
330       else
331         *ec = result;
332     }
333     return was_error;
334   }
335
336   bool is_empty_directory(const path& p)
337   {
338     return fs::directory_iterator(p)== end_dir_itr;
339   }
340
341   bool remove_directory(const path& p) // true if succeeds
342     { return BOOST_REMOVE_DIRECTORY(p.c_str()); }
343   
344   bool remove_file(const path& p) // true if succeeds
345     { return BOOST_DELETE_FILE(p.c_str()); }
346   
347   // called by remove and remove_all_aux
348   bool remove_file_or_directory(const path& p, fs::file_type type, error_code* ec)
349     // return true if file removed, false if not removed
350   {
351     if (type == fs::file_not_found)
352     {
353       if (ec != 0) ec->clear();
354       return false;
355     }
356
357     if (type == fs::directory_file
358 #     ifdef BOOST_WINDOWS_API
359         || type == fs::_detail_directory_symlink
360 #     endif
361       )
362     {
363       if (error(!remove_directory(p), p, ec, "boost::filesystem::remove"))
364         return false;
365     }
366     else
367     {
368       if (error(!remove_file(p), p, ec, "boost::filesystem::remove"))
369         return false;
370     }
371     return true;
372   }
373
374   boost::uintmax_t remove_all_aux(const path& p, fs::file_type type,
375     error_code* ec)
376   {
377     boost::uintmax_t count = 1;
378
379     if (type == fs::directory_file)  // but not a directory symlink
380     {
381       for (fs::directory_iterator itr(p);
382             itr != end_dir_itr; ++itr)
383       {
384         fs::file_type tmp_type = query_file_type(itr->path(), ec);
385         if (ec != 0 && *ec)
386           return count;
387         count += remove_all_aux(itr->path(), tmp_type, ec);
388       }
389     }
390     remove_file_or_directory(p, type, ec);
391     return count;
392   }
393
394 #ifdef BOOST_POSIX_API
395
396 //--------------------------------------------------------------------------------------//
397 //                                                                                      //
398 //                            POSIX-specific helpers                                    //
399 //                                                                                      //
400 //--------------------------------------------------------------------------------------//
401
402   const char dot = '.';
403
404   bool not_found_error(int errval)
405   {
406     return errno == ENOENT || errno == ENOTDIR;
407   }
408
409   bool // true if ok
410   copy_file_api(const std::string& from_p,
411     const std::string& to_p, bool fail_if_exists)
412   {
413     const std::size_t buf_sz = 32768;
414     boost::scoped_array<char> buf(new char [buf_sz]);
415     int infile=-1, outfile=-1;  // -1 means not open
416
417     // bug fixed: code previously did a stat()on the from_file first, but that
418     // introduced a gratuitous race condition; the stat()is now done after the open()
419
420     if ((infile = ::open(from_p.c_str(), O_RDONLY))< 0)
421       { return false; }
422
423     struct stat from_stat;
424     if (::stat(from_p.c_str(), &from_stat)!= 0)
425     { 
426       ::close(infile);
427       return false;
428     }
429
430     int oflag = O_CREAT | O_WRONLY | O_TRUNC;
431     if (fail_if_exists)
432       oflag |= O_EXCL;
433     if ((outfile = ::open(to_p.c_str(), oflag, from_stat.st_mode))< 0)
434     {
435       int open_errno = errno;
436       BOOST_ASSERT(infile >= 0);
437       ::close(infile);
438       errno = open_errno;
439       return false;
440     }
441
442     ssize_t sz, sz_read=1, sz_write;
443     while (sz_read > 0
444       && (sz_read = ::read(infile, buf.get(), buf_sz))> 0)
445     {
446       // Allow for partial writes - see Advanced Unix Programming (2nd Ed.),
447       // Marc Rochkind, Addison-Wesley, 2004, page 94
448       sz_write = 0;
449       do
450       {
451         if ((sz = ::write(outfile, buf.get() + sz_write,
452           sz_read - sz_write))< 0)
453         { 
454           sz_read = sz; // cause read loop termination
455           break;        //  and error to be thrown after closes
456         }
457         sz_write += sz;
458       } while (sz_write < sz_read);
459     }
460
461     if (::close(infile)< 0)sz_read = -1;
462     if (::close(outfile)< 0)sz_read = -1;
463
464     return sz_read >= 0;
465   }
466
467   inline fs::file_type query_file_type(const path& p, error_code* ec)
468   {
469     return fs::detail::symlink_status(p, ec).type();
470   }
471
472 # else
473
474 //--------------------------------------------------------------------------------------//
475 //                                                                                      //
476 //                            Windows-specific helpers                                  //
477 //                                                                                      //
478 //--------------------------------------------------------------------------------------//
479
480   const std::size_t buf_size=128;
481
482   const wchar_t dot = L'.';
483
484   bool not_found_error(int errval)
485   {
486     return errval == ERROR_FILE_NOT_FOUND
487       || errval == ERROR_PATH_NOT_FOUND
488       || errval == ERROR_INVALID_NAME  // "tools/jam/src/:sys:stat.h", "//foo"
489       || errval == ERROR_INVALID_DRIVE  // USB card reader with no card inserted
490       || errval == ERROR_NOT_READY  // CD/DVD drive with no disc inserted
491       || errval == ERROR_INVALID_PARAMETER  // ":sys:stat.h"
492       || errval == ERROR_BAD_PATHNAME  // "//nosuch" on Win64
493       || errval == ERROR_BAD_NETPATH;  // "//nosuch" on Win32
494   }
495
496 // some distributions of mingw as early as GLIBCXX__ 20110325 have _stricmp, but the
497 // offical 4.6.2 release with __GLIBCXX__ 20111026  doesn't. Play it safe for now, and
498 // only use _stricmp if _MSC_VER is defined
499 #if defined(_MSC_VER) // || (defined(__GLIBCXX__) && __GLIBCXX__ >= 20110325)
500 #  define BOOST_FILESYSTEM_STRICMP _stricmp
501 #else
502 #  define BOOST_FILESYSTEM_STRICMP strcmp
503 #endif
504
505   perms make_permissions(const path& p, DWORD attr)
506   {
507     perms prms = fs::owner_read | fs::group_read | fs::others_read;
508     if  ((attr & FILE_ATTRIBUTE_READONLY) == 0)
509       prms |= fs::owner_write | fs::group_write | fs::others_write;
510     if (BOOST_FILESYSTEM_STRICMP(p.extension().string().c_str(), ".exe") == 0
511       || BOOST_FILESYSTEM_STRICMP(p.extension().string().c_str(), ".com") == 0
512       || BOOST_FILESYSTEM_STRICMP(p.extension().string().c_str(), ".bat") == 0
513       || BOOST_FILESYSTEM_STRICMP(p.extension().string().c_str(), ".cmd") == 0)
514       prms |= fs::owner_exe | fs::group_exe | fs::others_exe;
515     return prms;
516   }
517
518   // these constants come from inspecting some Microsoft sample code
519   std::time_t to_time_t(const FILETIME & ft)
520   {
521     __int64 t = (static_cast<__int64>(ft.dwHighDateTime)<< 32)
522       + ft.dwLowDateTime;
523 #   if !defined(BOOST_MSVC) || BOOST_MSVC > 1300 // > VC++ 7.0
524     t -= 116444736000000000LL;
525 #   else
526     t -= 116444736000000000;
527 #   endif
528     t /= 10000000;
529     return static_cast<std::time_t>(t);
530   }
531
532   void to_FILETIME(std::time_t t, FILETIME & ft)
533   {
534     __int64 temp = t;
535     temp *= 10000000;
536 #   if !defined(BOOST_MSVC) || BOOST_MSVC > 1300 // > VC++ 7.0
537     temp += 116444736000000000LL;
538 #   else
539     temp += 116444736000000000;
540 #   endif
541     ft.dwLowDateTime = static_cast<DWORD>(temp);
542     ft.dwHighDateTime = static_cast<DWORD>(temp >> 32);
543   }
544
545   // Thanks to Jeremy Maitin-Shepard for much help and for permission to
546   // base the equivalent()implementation on portions of his 
547   // file-equivalence-win32.cpp experimental code.
548
549   struct handle_wrapper
550   {
551     HANDLE handle;
552     handle_wrapper(HANDLE h)
553       : handle(h){}
554     ~handle_wrapper()
555     {
556       if (handle != INVALID_HANDLE_VALUE)
557         ::CloseHandle(handle);
558     }
559   };
560
561   HANDLE create_file_handle(const path& p, DWORD dwDesiredAccess,
562     DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes,
563     DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes,
564     HANDLE hTemplateFile)
565   {
566     return ::CreateFileW(p.c_str(), dwDesiredAccess, dwShareMode,
567       lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
568       hTemplateFile);
569   }
570
571   bool is_reparse_point_a_symlink(const path& p)
572   {
573     handle_wrapper h(create_file_handle(p, FILE_READ_EA,
574       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
575       FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL));
576     if (h.handle == INVALID_HANDLE_VALUE)
577       return false;
578
579     boost::scoped_array<char> buf(new char [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);    
580  
581     // Query the reparse data
582     DWORD dwRetLen;
583     BOOL result = ::DeviceIoControl(h.handle, FSCTL_GET_REPARSE_POINT, NULL, 0, buf.get(),
584       MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &dwRetLen, NULL);
585     if (!result) return false;
586
587     return reinterpret_cast<const REPARSE_DATA_BUFFER*>(buf.get())->ReparseTag
588         == IO_REPARSE_TAG_SYMLINK
589         // Issue 9016 asked that NTFS directory junctions be recognized as directories.
590         // That is equivalent to recognizing them as symlinks, and then the normal symlink
591         // mechanism will take care of recognizing them as directories.
592         //
593         // Directory junctions are very similar to symlinks, but have some performance
594         // and other advantages over symlinks. They can be created from the command line
595         // with "mklink /j junction-name target-path".
596       || reinterpret_cast<const REPARSE_DATA_BUFFER*>(buf.get())->ReparseTag
597         == IO_REPARSE_TAG_MOUNT_POINT;  // aka "directory junction" or "junction"
598   }
599
600   inline std::size_t get_full_path_name(
601     const path& src, std::size_t len, wchar_t* buf, wchar_t** p)
602   {
603     return static_cast<std::size_t>(
604       ::GetFullPathNameW(src.c_str(), static_cast<DWORD>(len), buf, p));
605   }
606
607   fs::file_status process_status_failure(const path& p, error_code* ec)
608   {
609     int errval(::GetLastError());
610     if (ec != 0)                             // always report errval, even though some
611       ec->assign(errval, system_category());   // errval values are not status_errors
612
613     if (not_found_error(errval))
614     {
615       return fs::file_status(fs::file_not_found, fs::no_perms);
616     }
617     else if ((errval == ERROR_SHARING_VIOLATION))
618     {
619       return fs::file_status(fs::type_unknown);
620     }
621     if (ec == 0)
622       BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::status",
623         p, error_code(errval, system_category())));
624     return fs::file_status(fs::status_error);
625   }
626
627   //  differs from symlink_status() in that directory symlinks are reported as
628   //  _detail_directory_symlink, as required on Windows by remove() and its helpers.
629   fs::file_type query_file_type(const path& p, error_code* ec)
630   {
631     DWORD attr(::GetFileAttributesW(p.c_str()));
632     if (attr == 0xFFFFFFFF)
633     {
634       return process_status_failure(p, ec).type();
635     }
636
637     if (ec != 0) ec->clear();
638
639     if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
640     {
641       if (is_reparse_point_a_symlink(p))
642         return (attr & FILE_ATTRIBUTE_DIRECTORY)
643           ? fs::_detail_directory_symlink
644           : fs::symlink_file;
645       return fs::reparse_file;
646     }
647
648     return (attr & FILE_ATTRIBUTE_DIRECTORY)
649       ? fs::directory_file
650       : fs::regular_file;
651   }
652
653   BOOL resize_file_api(const wchar_t* p, boost::uintmax_t size)
654   {
655     HANDLE handle = CreateFileW(p, GENERIC_WRITE, 0, 0, OPEN_EXISTING,
656                                 FILE_ATTRIBUTE_NORMAL, 0);
657     LARGE_INTEGER sz;
658     sz.QuadPart = size;
659     return handle != INVALID_HANDLE_VALUE
660       && ::SetFilePointerEx(handle, sz, 0, FILE_BEGIN)
661       && ::SetEndOfFile(handle)
662       && ::CloseHandle(handle);
663   }
664
665   //  Windows kernel32.dll functions that may or may not be present
666   //  must be accessed through pointers
667
668   typedef BOOL (WINAPI *PtrCreateHardLinkW)(
669     /*__in*/       LPCWSTR lpFileName,
670     /*__in*/       LPCWSTR lpExistingFileName,
671     /*__reserved*/ LPSECURITY_ATTRIBUTES lpSecurityAttributes
672    );
673
674   PtrCreateHardLinkW create_hard_link_api = PtrCreateHardLinkW(
675     ::GetProcAddress(
676       ::GetModuleHandle(TEXT("kernel32.dll")), "CreateHardLinkW"));
677
678   typedef BOOLEAN (WINAPI *PtrCreateSymbolicLinkW)(
679     /*__in*/ LPCWSTR lpSymlinkFileName,
680     /*__in*/ LPCWSTR lpTargetFileName,
681     /*__in*/ DWORD dwFlags
682    );
683
684   PtrCreateSymbolicLinkW create_symbolic_link_api = PtrCreateSymbolicLinkW(
685     ::GetProcAddress(
686       ::GetModuleHandle(TEXT("kernel32.dll")), "CreateSymbolicLinkW"));
687
688 #endif
689
690 //#ifdef BOOST_WINDOWS_API
691 //
692 //
693 //  inline bool get_free_disk_space(const std::wstring& ph,
694 //    PULARGE_INTEGER avail, PULARGE_INTEGER total, PULARGE_INTEGER free)
695 //    { return ::GetDiskFreeSpaceExW(ph.c_str(), avail, total, free)!= 0; }
696 //
697 //#endif
698
699 } // unnamed namespace
700
701 //--------------------------------------------------------------------------------------//
702 //                                                                                      //
703 //                operations functions declared in operations.hpp                       //
704 //                            in alphabetic order                                       //
705 //                                                                                      //
706 //--------------------------------------------------------------------------------------//
707
708 namespace boost
709 {
710 namespace filesystem
711 {
712
713   BOOST_FILESYSTEM_DECL
714   path absolute(const path& p, const path& base)
715   {
716 //    if ( p.empty() || p.is_absolute() )
717 //      return p;
718 //    //  recursively calling absolute is sub-optimal, but is simple
719 //    path abs_base(base.is_absolute() ? base : absolute(base));
720 //# ifdef BOOST_WINDOWS_API
721 //    if (p.has_root_directory())
722 //      return abs_base.root_name() / p;
723 //    //  !p.has_root_directory
724 //    if (p.has_root_name())
725 //      return p.root_name()
726 //        / abs_base.root_directory() / abs_base.relative_path() / p.relative_path();
727 //    //  !p.has_root_name()
728 //# endif
729 //    return abs_base / p;
730
731     //  recursively calling absolute is sub-optimal, but is sure and simple
732     path abs_base(base.is_absolute() ? base : absolute(base));
733
734     //  store expensive to compute values that are needed multiple times
735     path p_root_name (p.root_name());
736     path base_root_name (abs_base.root_name());
737     path p_root_directory (p.root_directory());
738
739     if (p.empty())
740       return abs_base;
741
742     if (!p_root_name.empty())  // p.has_root_name()
743     {
744       if (p_root_directory.empty())  // !p.has_root_directory()
745         return p_root_name / abs_base.root_directory()
746         / abs_base.relative_path() / p.relative_path();
747       // p is absolute, so fall through to return p at end of block
748     }
749
750     else if (!p_root_directory.empty())  // p.has_root_directory()
751     {
752 #     ifdef BOOST_POSIX_API
753       // POSIX can have root name it it is a network path
754       if (base_root_name.empty())   // !abs_base.has_root_name()
755         return p;
756 #     endif
757       return base_root_name / p;
758     }
759
760     else
761     {
762       return abs_base / p;
763     }
764
765     return p;  // p.is_absolute() is true
766   }
767
768 namespace detail
769 {
770   BOOST_FILESYSTEM_DECL bool possible_large_file_size_support()
771   {
772 #   ifdef BOOST_POSIX_API
773     struct stat lcl_stat;
774     return sizeof(lcl_stat.st_size)> 4;
775 #   else
776     return true;
777 #   endif
778   }
779
780   BOOST_FILESYSTEM_DECL
781   path canonical(const path& p, const path& base, system::error_code* ec)
782   {
783     path source (p.is_absolute() ? p : absolute(p, base));
784     path root(source.root_path());
785     path result;
786
787     system::error_code local_ec;
788     file_status stat (status(source, local_ec));
789
790     if (stat.type() == fs::file_not_found)
791     {
792       if (ec == 0)
793         BOOST_FILESYSTEM_THROW(filesystem_error(
794           "boost::filesystem::canonical", source,
795           error_code(system::errc::no_such_file_or_directory, system::generic_category())));
796       ec->assign(system::errc::no_such_file_or_directory, system::generic_category());
797       return result;
798     }
799     else if (local_ec)
800     {
801       if (ec == 0)
802         BOOST_FILESYSTEM_THROW(filesystem_error(
803           "boost::filesystem::canonical", source, local_ec));
804       *ec = local_ec;
805       return result;
806     }
807
808     bool scan (true);
809     while (scan)
810     {
811       scan = false;
812       result.clear();
813       for (path::iterator itr = source.begin(); itr != source.end(); ++itr)
814       {
815         if (*itr == dot_path())
816           continue;
817         if (*itr == dot_dot_path())
818         {
819           if (result != root)
820             result.remove_filename();
821           continue;
822         }
823
824         result /= *itr;
825
826         bool is_sym (is_symlink(detail::symlink_status(result, ec)));
827         if (ec && *ec)
828           return path();
829
830         if (is_sym)
831         {
832           path link(detail::read_symlink(result, ec));
833           if (ec && *ec)
834             return path();
835           result.remove_filename();
836
837           if (link.is_absolute())
838           {
839             for (++itr; itr != source.end(); ++itr)
840               link /= *itr;
841             source = link;
842           }
843           else // link is relative
844           {
845             path new_source(result);
846             new_source /= link;
847             for (++itr; itr != source.end(); ++itr)
848               new_source /= *itr;
849             source = new_source;
850           }
851           scan = true;  // symlink causes scan to be restarted
852           break;
853         }
854       }
855     }
856     if (ec != 0)
857       ec->clear();
858     BOOST_ASSERT_MSG(result.is_absolute(), "canonical() implementation error; please report");
859     return result;
860   }
861
862   BOOST_FILESYSTEM_DECL
863   void copy(const path& from, const path& to, system::error_code* ec)
864   {
865     file_status s(symlink_status(from, *ec));
866     if (ec != 0 && *ec) return;
867
868     if(is_symlink(s))
869     {
870       copy_symlink(from, to, *ec);
871     }
872     else if(is_directory(s))
873     {
874       copy_directory(from, to, *ec);
875     }
876     else if(is_regular_file(s))
877     {
878       copy_file(from, to, fs::copy_option::fail_if_exists, *ec);
879     }
880     else
881     {
882       if (ec == 0)
883         BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::copy",
884           from, to, error_code(BOOST_ERROR_NOT_SUPPORTED, system_category())));
885       ec->assign(BOOST_ERROR_NOT_SUPPORTED, system_category());
886     }
887   }
888
889   BOOST_FILESYSTEM_DECL
890   void copy_directory(const path& from, const path& to, system::error_code* ec)
891   {
892 #   ifdef BOOST_POSIX_API
893     struct stat from_stat;
894 #   endif
895     error(!BOOST_COPY_DIRECTORY(from.c_str(), to.c_str()),
896       from, to, ec, "boost::filesystem::copy_directory");
897   }
898
899   BOOST_FILESYSTEM_DECL
900   void copy_file(const path& from, const path& to, copy_option option, error_code* ec)
901   {
902     error(!BOOST_COPY_FILE(from.c_str(), to.c_str(),
903       option == fail_if_exists),
904         from, to, ec, "boost::filesystem::copy_file");
905   }
906
907   BOOST_FILESYSTEM_DECL
908   void copy_symlink(const path& existing_symlink, const path& new_symlink,
909     system::error_code* ec)
910   {
911 # if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0600
912     error(true, error_code(BOOST_ERROR_NOT_SUPPORTED, system_category()),
913       new_symlink, existing_symlink, ec,
914       "boost::filesystem::copy_symlink");
915
916 # else  // modern Windows or BOOST_POSIX_API 
917     path p(read_symlink(existing_symlink, ec));
918     if (ec != 0 && *ec) return;
919     create_symlink(p, new_symlink, ec);
920
921 # endif
922   }
923
924  BOOST_FILESYSTEM_DECL
925   bool create_directories(const path& p, system::error_code* ec)
926   {
927     error_code local_ec;
928     file_status p_status = status(p, local_ec);
929
930     if (p_status.type() == directory_file)
931     {
932       if (ec != 0)
933         ec->clear();
934       return false;
935     }
936
937     path parent = p.parent_path();
938     BOOST_ASSERT_MSG(parent != p, "internal error: p == p.parent_path()");
939     if (!parent.empty())
940     {
941       // determine if the parent exists
942       file_status parent_status = status(parent, local_ec);
943
944       // if the parent does not exist, create the parent
945       if (parent_status.type() == file_not_found)
946       {
947         create_directories(parent, local_ec);
948         if (local_ec)
949         {
950           if (ec == 0)
951             BOOST_FILESYSTEM_THROW(filesystem_error(
952               "boost::filesystem::create_directories", parent, local_ec));
953           else
954             *ec = local_ec;
955           return false;
956         }
957       }
958     }
959
960     // create the directory
961     return create_directory(p, ec);
962   }
963
964   BOOST_FILESYSTEM_DECL
965   bool create_directory(const path& p, error_code* ec)
966   {
967     if (BOOST_CREATE_DIRECTORY(p.c_str()))
968     {
969       if (ec != 0)
970         ec->clear();
971       return true;
972     }
973
974     //  attempt to create directory failed
975     int errval(BOOST_ERRNO);  // save reason for failure
976     error_code dummy;
977     if (errval == BOOST_ERROR_ALREADY_EXISTS && is_directory(p, dummy))
978     {
979       if (ec != 0)
980         ec->clear();
981       return false;
982     }
983
984     //  attempt to create directory failed && it doesn't already exist
985     if (ec == 0)
986       BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::create_directory",
987         p, error_code(errval, system_category())));
988     else
989       ec->assign(errval, system_category());
990     return false;
991   }
992
993   BOOST_FILESYSTEM_DECL
994   void create_directory_symlink(const path& to, const path& from,
995                                  system::error_code* ec)
996   {
997 #   if defined(BOOST_WINDOWS_API) && _WIN32_WINNT < 0x0600  // SDK earlier than Vista and Server 2008
998
999     error(true, error_code(BOOST_ERROR_NOT_SUPPORTED, system_category()), to, from, ec,
1000       "boost::filesystem::create_directory_symlink");
1001 #   else
1002
1003 #     if defined(BOOST_WINDOWS_API) && _WIN32_WINNT >= 0x0600
1004         // see if actually supported by Windows runtime dll
1005         if (error(!create_symbolic_link_api,
1006             error_code(BOOST_ERROR_NOT_SUPPORTED, system_category()),
1007             to, from, ec,
1008             "boost::filesystem::create_directory_symlink"))
1009           return;
1010 #     endif
1011
1012     error(!BOOST_CREATE_SYMBOLIC_LINK(from.c_str(), to.c_str(), SYMBOLIC_LINK_FLAG_DIRECTORY),
1013       to, from, ec, "boost::filesystem::create_directory_symlink");
1014 #   endif
1015   }
1016
1017   BOOST_FILESYSTEM_DECL
1018   void create_hard_link(const path& to, const path& from, error_code* ec)
1019   {
1020
1021 #   if defined(BOOST_WINDOWS_API) && _WIN32_WINNT < 0x0500  // SDK earlier than Win 2K
1022
1023     error(true, error_code(BOOST_ERROR_NOT_SUPPORTED, system_category()), to, from, ec,
1024       "boost::filesystem::create_hard_link");
1025 #   else
1026
1027 #     if defined(BOOST_WINDOWS_API) && _WIN32_WINNT >= 0x0500
1028         // see if actually supported by Windows runtime dll
1029         if (error(!create_hard_link_api,
1030             error_code(BOOST_ERROR_NOT_SUPPORTED, system_category()),
1031             to, from, ec,
1032             "boost::filesystem::create_hard_link"))
1033           return;
1034 #     endif
1035
1036     error(!BOOST_CREATE_HARD_LINK(from.c_str(), to.c_str()), to, from, ec,
1037       "boost::filesystem::create_hard_link");
1038 #   endif
1039   }
1040
1041   BOOST_FILESYSTEM_DECL
1042   void create_symlink(const path& to, const path& from, error_code* ec)
1043   {
1044 #   if defined(BOOST_WINDOWS_API) && _WIN32_WINNT < 0x0600  // SDK earlier than Vista and Server 2008
1045     error(true, error_code(BOOST_ERROR_NOT_SUPPORTED, system_category()), to, from, ec,
1046       "boost::filesystem::create_directory_symlink");
1047 #   else
1048
1049 #     if defined(BOOST_WINDOWS_API) && _WIN32_WINNT >= 0x0600
1050         // see if actually supported by Windows runtime dll
1051         if (error(!create_symbolic_link_api,
1052             error_code(BOOST_ERROR_NOT_SUPPORTED, system_category()),
1053             to, from, ec,
1054             "boost::filesystem::create_symlink"))
1055           return;
1056 #     endif
1057
1058     error(!BOOST_CREATE_SYMBOLIC_LINK(from.c_str(), to.c_str(), 0),
1059       to, from, ec, "boost::filesystem::create_symlink");
1060 #   endif
1061   }
1062
1063   BOOST_FILESYSTEM_DECL
1064   path current_path(error_code* ec)
1065   {
1066 #   ifdef BOOST_POSIX_API
1067     path cur;
1068     for (long path_max = 128;; path_max *=2)// loop 'til buffer large enough
1069     {
1070       boost::scoped_array<char>
1071         buf(new char[static_cast<std::size_t>(path_max)]);
1072       if (::getcwd(buf.get(), static_cast<std::size_t>(path_max))== 0)
1073       {
1074         if (error(errno != ERANGE
1075       // bug in some versions of the Metrowerks C lib on the Mac: wrong errno set 
1076 #         if defined(__MSL__) && (defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__))
1077           && errno != 0
1078 #         endif
1079           , ec, "boost::filesystem::current_path"))
1080         {
1081           break;
1082         }
1083       }
1084       else
1085       {
1086         cur = buf.get();
1087         if (ec != 0) ec->clear();
1088         break;
1089       }
1090     }
1091     return cur;
1092
1093 #   else
1094     DWORD sz;
1095     if ((sz = ::GetCurrentDirectoryW(0, NULL))== 0)sz = 1;
1096     boost::scoped_array<path::value_type> buf(new path::value_type[sz]);
1097     error(::GetCurrentDirectoryW(sz, buf.get())== 0, ec,
1098       "boost::filesystem::current_path");
1099     return path(buf.get());
1100 #   endif
1101   }
1102
1103
1104   BOOST_FILESYSTEM_DECL
1105   void current_path(const path& p, system::error_code* ec)
1106   {
1107     error(!BOOST_SET_CURRENT_DIRECTORY(p.c_str()),
1108       p, ec, "boost::filesystem::current_path");
1109   }
1110
1111   BOOST_FILESYSTEM_DECL
1112   bool equivalent(const path& p1, const path& p2, system::error_code* ec)
1113   {
1114 #   ifdef BOOST_POSIX_API
1115     struct stat s2;
1116     int e2(::stat(p2.c_str(), &s2));
1117     struct stat s1;
1118     int e1(::stat(p1.c_str(), &s1));
1119
1120     if (e1 != 0 || e2 != 0)
1121     {
1122       // if one is invalid and the other isn't then they aren't equivalent,
1123       // but if both are invalid then it is an error
1124       error (e1 != 0 && e2 != 0, p1, p2, ec, "boost::filesystem::equivalent");
1125       return false;
1126     }
1127
1128     // both stats now known to be valid
1129     return  s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino
1130         // According to the POSIX stat specs, "The st_ino and st_dev fields
1131         // taken together uniquely identify the file within the system."
1132         // Just to be sure, size and mod time are also checked.
1133         && s1.st_size == s2.st_size && s1.st_mtime == s2.st_mtime;
1134
1135 #   else  // Windows
1136
1137     // Note well: Physical location on external media is part of the
1138     // equivalence criteria. If there are no open handles, physical location
1139     // can change due to defragmentation or other relocations. Thus handles
1140     // must be held open until location information for both paths has
1141     // been retrieved.
1142
1143     // p2 is done first, so any error reported is for p1
1144     handle_wrapper h2(
1145       create_file_handle(
1146           p2.c_str(),
1147           0,
1148           FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
1149           0,
1150           OPEN_EXISTING,
1151           FILE_FLAG_BACKUP_SEMANTICS,
1152           0));
1153
1154     handle_wrapper h1(
1155       create_file_handle(
1156           p1.c_str(),
1157           0,
1158           FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
1159           0,
1160           OPEN_EXISTING,
1161           FILE_FLAG_BACKUP_SEMANTICS,
1162           0));
1163
1164     if (h1.handle == INVALID_HANDLE_VALUE
1165       || h2.handle == INVALID_HANDLE_VALUE)
1166     {
1167       // if one is invalid and the other isn't, then they aren't equivalent,
1168       // but if both are invalid then it is an error
1169       error(h1.handle == INVALID_HANDLE_VALUE
1170         && h2.handle == INVALID_HANDLE_VALUE, p1, p2, ec,
1171           "boost::filesystem::equivalent");
1172       return false;
1173     }
1174
1175     // at this point, both handles are known to be valid
1176
1177     BY_HANDLE_FILE_INFORMATION info1, info2;
1178
1179     if (error(!::GetFileInformationByHandle(h1.handle, &info1),
1180       p1, p2, ec, "boost::filesystem::equivalent"))
1181         return  false;
1182
1183     if (error(!::GetFileInformationByHandle(h2.handle, &info2),
1184       p1, p2, ec, "boost::filesystem::equivalent"))
1185         return  false;
1186
1187     // In theory, volume serial numbers are sufficient to distinguish between
1188     // devices, but in practice VSN's are sometimes duplicated, so last write
1189     // time and file size are also checked.
1190       return 
1191         info1.dwVolumeSerialNumber == info2.dwVolumeSerialNumber
1192         && info1.nFileIndexHigh == info2.nFileIndexHigh
1193         && info1.nFileIndexLow == info2.nFileIndexLow
1194         && info1.nFileSizeHigh == info2.nFileSizeHigh
1195         && info1.nFileSizeLow == info2.nFileSizeLow
1196         && info1.ftLastWriteTime.dwLowDateTime
1197           == info2.ftLastWriteTime.dwLowDateTime
1198         && info1.ftLastWriteTime.dwHighDateTime
1199           == info2.ftLastWriteTime.dwHighDateTime;
1200
1201 #   endif
1202   }
1203
1204   BOOST_FILESYSTEM_DECL
1205   boost::uintmax_t file_size(const path& p, error_code* ec)
1206   {
1207 #   ifdef BOOST_POSIX_API
1208
1209     struct stat path_stat;
1210     if (error(::stat(p.c_str(), &path_stat)!= 0,
1211         p, ec, "boost::filesystem::file_size"))
1212       return static_cast<boost::uintmax_t>(-1);
1213    if (error(!S_ISREG(path_stat.st_mode),
1214       error_code(EPERM, system_category()),
1215         p, ec, "boost::filesystem::file_size"))
1216       return static_cast<boost::uintmax_t>(-1);
1217
1218     return static_cast<boost::uintmax_t>(path_stat.st_size);
1219
1220 #   else  // Windows
1221
1222     // assume uintmax_t is 64-bits on all Windows compilers
1223
1224     WIN32_FILE_ATTRIBUTE_DATA fad;
1225
1226     if (error(::GetFileAttributesExW(p.c_str(), ::GetFileExInfoStandard, &fad)== 0,
1227         p, ec, "boost::filesystem::file_size"))
1228         return static_cast<boost::uintmax_t>(-1);
1229
1230     if (error((fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)!= 0,
1231       error_code(ERROR_NOT_SUPPORTED, system_category()),
1232         p, ec, "boost::filesystem::file_size"))
1233       return static_cast<boost::uintmax_t>(-1);
1234
1235     return (static_cast<boost::uintmax_t>(fad.nFileSizeHigh)
1236               << (sizeof(fad.nFileSizeLow)*8)) + fad.nFileSizeLow;
1237 #   endif
1238   }
1239
1240   BOOST_FILESYSTEM_DECL
1241   boost::uintmax_t hard_link_count(const path& p, system::error_code* ec)
1242   {
1243 #   ifdef BOOST_POSIX_API
1244
1245     struct stat path_stat;
1246     return error(::stat(p.c_str(), &path_stat)!= 0,
1247                   p, ec, "boost::filesystem::hard_link_count")
1248            ? 0
1249            : static_cast<boost::uintmax_t>(path_stat.st_nlink);
1250
1251 #   else // Windows
1252
1253     // Link count info is only available through GetFileInformationByHandle
1254     BY_HANDLE_FILE_INFORMATION info;
1255     handle_wrapper h(
1256       create_file_handle(p.c_str(), 0,
1257           FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
1258           OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0));
1259     return
1260       !error(h.handle == INVALID_HANDLE_VALUE,
1261               p, ec, "boost::filesystem::hard_link_count")
1262       && !error(::GetFileInformationByHandle(h.handle, &info)== 0,
1263                  p, ec, "boost::filesystem::hard_link_count")
1264            ? info.nNumberOfLinks
1265            : 0;
1266 #   endif
1267   }
1268
1269   BOOST_FILESYSTEM_DECL
1270   path initial_path(error_code* ec)
1271   {
1272       static path init_path;
1273       if (init_path.empty())
1274         init_path = current_path(ec);
1275       else if (ec != 0) ec->clear();
1276       return init_path;
1277   }
1278
1279   BOOST_FILESYSTEM_DECL
1280   bool is_empty(const path& p, system::error_code* ec)
1281   {
1282 #   ifdef BOOST_POSIX_API
1283
1284     struct stat path_stat;
1285     if (error(::stat(p.c_str(), &path_stat)!= 0,
1286         p, ec, "boost::filesystem::is_empty"))
1287       return false;        
1288     return S_ISDIR(path_stat.st_mode)
1289       ? is_empty_directory(p)
1290       : path_stat.st_size == 0;
1291 #   else
1292
1293     WIN32_FILE_ATTRIBUTE_DATA fad;
1294     if (error(::GetFileAttributesExW(p.c_str(), ::GetFileExInfoStandard, &fad)== 0,
1295       p, ec, "boost::filesystem::is_empty"))
1296         return false;
1297
1298     if (ec != 0) ec->clear();
1299     return 
1300       (fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1301         ? is_empty_directory(p)
1302         : (!fad.nFileSizeHigh && !fad.nFileSizeLow);
1303 #   endif
1304   }
1305
1306   BOOST_FILESYSTEM_DECL
1307   std::time_t last_write_time(const path& p, system::error_code* ec)
1308   {
1309 #   ifdef BOOST_POSIX_API
1310
1311     struct stat path_stat;
1312     if (error(::stat(p.c_str(), &path_stat)!= 0,
1313       p, ec, "boost::filesystem::last_write_time"))
1314         return std::time_t(-1);
1315     return path_stat.st_mtime;
1316
1317 #   else
1318
1319     handle_wrapper hw(
1320       create_file_handle(p.c_str(), 0,
1321         FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
1322         OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0));
1323
1324     if (error(hw.handle == INVALID_HANDLE_VALUE,
1325       p, ec, "boost::filesystem::last_write_time"))
1326         return std::time_t(-1);
1327
1328     FILETIME lwt;
1329
1330     if (error(::GetFileTime(hw.handle, 0, 0, &lwt)== 0,
1331       p, ec, "boost::filesystem::last_write_time"))
1332         return std::time_t(-1);
1333
1334     return to_time_t(lwt);
1335 #   endif
1336   }
1337
1338   BOOST_FILESYSTEM_DECL
1339   void last_write_time(const path& p, const std::time_t new_time,
1340                         system::error_code* ec)
1341   {
1342 #   ifdef BOOST_POSIX_API
1343
1344     struct stat path_stat;
1345     if (error(::stat(p.c_str(), &path_stat)!= 0,
1346       p, ec, "boost::filesystem::last_write_time"))
1347         return;
1348     ::utimbuf buf;
1349     buf.actime = path_stat.st_atime; // utime()updates access time too:-(
1350     buf.modtime = new_time;
1351     error(::utime(p.c_str(), &buf)!= 0,
1352       p, ec, "boost::filesystem::last_write_time");
1353
1354 #   else
1355
1356     handle_wrapper hw(
1357       create_file_handle(p.c_str(), FILE_WRITE_ATTRIBUTES,
1358         FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
1359         OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0));
1360
1361     if (error(hw.handle == INVALID_HANDLE_VALUE,
1362       p, ec, "boost::filesystem::last_write_time"))
1363         return;
1364
1365     FILETIME lwt;
1366     to_FILETIME(new_time, lwt);
1367
1368     error(::SetFileTime(hw.handle, 0, 0, &lwt)== 0,
1369       p, ec, "boost::filesystem::last_write_time");
1370 #   endif
1371   }
1372
1373 # ifdef BOOST_POSIX_API
1374     const perms active_bits(all_all | set_uid_on_exe | set_gid_on_exe | sticky_bit);
1375     inline mode_t mode_cast(perms prms) { return prms & active_bits; }
1376 # endif
1377
1378   BOOST_FILESYSTEM_DECL
1379   void permissions(const path& p, perms prms, system::error_code* ec)
1380   {
1381     BOOST_ASSERT_MSG(!((prms & add_perms) && (prms & remove_perms)),
1382       "add_perms and remove_perms are mutually exclusive");
1383
1384     if ((prms & add_perms) && (prms & remove_perms))  // precondition failed
1385       return;
1386
1387 # ifdef BOOST_POSIX_API
1388     error_code local_ec;
1389     file_status current_status((prms & symlink_perms)
1390                                ? fs::symlink_status(p, local_ec)
1391                                : fs::status(p, local_ec));
1392     if (local_ec)
1393     {
1394       if (ec == 0)
1395       BOOST_FILESYSTEM_THROW(filesystem_error(
1396           "boost::filesystem::permissions", p, local_ec));
1397       else
1398         *ec = local_ec;
1399       return;
1400     }
1401
1402     if (prms & add_perms)
1403       prms |= current_status.permissions();
1404     else if (prms & remove_perms)
1405       prms = current_status.permissions() & ~prms;
1406
1407     // Mac OS X Lion and some other platforms don't support fchmodat().
1408     // Solaris (SunPro and gcc) only support fchmodat() on Solaris 11 and higher,
1409     // and a runtime check is too much trouble.
1410     // Linux does not support permissions on symbolic links and has no plans to
1411     // support them in the future.  The chmod() code is thus more practical,
1412     // rather than always hitting ENOTSUP when sending in AT_SYMLINK_NO_FOLLOW.
1413     //  - See the 3rd paragraph of
1414     // "Symbolic link ownership, permissions, and timestamps" at:
1415     //   "http://man7.org/linux/man-pages/man7/symlink.7.html"
1416     //  - See the fchmodat() Linux man page:
1417     //   "http://man7.org/linux/man-pages/man2/fchmodat.2.html"
1418 #   if defined(AT_FDCWD) && defined(AT_SYMLINK_NOFOLLOW) \
1419       && !(defined(__SUNPRO_CC) || defined(__sun) || defined(sun)) \
1420       && !(defined(linux) || defined(__linux) || defined(__linux__))
1421       if (::fchmodat(AT_FDCWD, p.c_str(), mode_cast(prms),
1422            !(prms & symlink_perms) ? 0 : AT_SYMLINK_NOFOLLOW))
1423 #   else  // fallback if fchmodat() not supported
1424       if (::chmod(p.c_str(), mode_cast(prms)))
1425 #   endif
1426     {
1427       if (ec == 0)
1428       BOOST_FILESYSTEM_THROW(filesystem_error(
1429           "boost::filesystem::permissions", p,
1430           error_code(errno, system::generic_category())));
1431       else
1432         ec->assign(errno, system::generic_category());
1433     }
1434
1435 # else  // Windows
1436
1437     // if not going to alter FILE_ATTRIBUTE_READONLY, just return
1438     if (!(!((prms & (add_perms | remove_perms)))
1439       || (prms & (owner_write|group_write|others_write))))
1440       return;
1441
1442     DWORD attr = ::GetFileAttributesW(p.c_str());
1443
1444     if (error(attr == 0, p, ec, "boost::filesystem::permissions"))
1445       return;
1446
1447     if (prms & add_perms)
1448       attr &= ~FILE_ATTRIBUTE_READONLY;
1449     else if (prms & remove_perms)
1450       attr |= FILE_ATTRIBUTE_READONLY;
1451     else if (prms & (owner_write|group_write|others_write))
1452       attr &= ~FILE_ATTRIBUTE_READONLY;
1453     else
1454       attr |= FILE_ATTRIBUTE_READONLY;
1455
1456     error(::SetFileAttributesW(p.c_str(), attr) == 0,
1457       p, ec, "boost::filesystem::permissions");
1458 # endif
1459   }
1460
1461   BOOST_FILESYSTEM_DECL
1462   path read_symlink(const path& p, system::error_code* ec)
1463   {
1464     path symlink_path;
1465
1466 #   ifdef BOOST_POSIX_API
1467
1468     for (std::size_t path_max = 64;; path_max *= 2)// loop 'til buffer large enough
1469     {
1470       boost::scoped_array<char> buf(new char[path_max]);
1471       ssize_t result;
1472       if ((result=::readlink(p.c_str(), buf.get(), path_max))== -1)
1473       {
1474         if (ec == 0)
1475           BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::read_symlink",
1476             p, error_code(errno, system_category())));
1477         else ec->assign(errno, system_category());
1478         break;
1479       }
1480       else
1481       {
1482         if(result != static_cast<ssize_t>(path_max))
1483         {
1484           symlink_path.assign(buf.get(), buf.get() + result);
1485           if (ec != 0) ec->clear();
1486           break;
1487         }
1488       }
1489     }
1490
1491 #   elif _WIN32_WINNT < 0x0600  // SDK earlier than Vista and Server 2008
1492     error(true, error_code(BOOST_ERROR_NOT_SUPPORTED, system_category()), p, ec,
1493           "boost::filesystem::read_symlink");
1494 #   else  // Vista and Server 2008 SDK, or later
1495
1496     union info_t
1497     {
1498       char buf[REPARSE_DATA_BUFFER_HEADER_SIZE+MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
1499       REPARSE_DATA_BUFFER rdb;
1500     } info;
1501
1502     handle_wrapper h(
1503       create_file_handle(p.c_str(), GENERIC_READ, 0, 0, OPEN_EXISTING,
1504         FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0));
1505
1506     if (error(h.handle == INVALID_HANDLE_VALUE, p, ec, "boost::filesystem::read_symlink"))
1507       return symlink_path;
1508
1509     DWORD sz;
1510
1511     if (!error(::DeviceIoControl(h.handle, FSCTL_GET_REPARSE_POINT,
1512           0, 0, info.buf, sizeof(info), &sz, 0) == 0, p, ec,
1513           "boost::filesystem::read_symlink" ))
1514       symlink_path.assign(
1515         static_cast<wchar_t*>(info.rdb.SymbolicLinkReparseBuffer.PathBuffer)
1516         + info.rdb.SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(wchar_t),
1517         static_cast<wchar_t*>(info.rdb.SymbolicLinkReparseBuffer.PathBuffer)
1518         + info.rdb.SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(wchar_t)
1519         + info.rdb.SymbolicLinkReparseBuffer.PrintNameLength/sizeof(wchar_t));
1520 #     endif
1521     return symlink_path;
1522   }
1523   
1524   BOOST_FILESYSTEM_DECL
1525   bool remove(const path& p, error_code* ec)
1526   {
1527     error_code tmp_ec;
1528     file_type type = query_file_type(p, &tmp_ec);
1529     if (error(type == status_error, tmp_ec, p, ec,
1530         "boost::filesystem::remove"))
1531       return false;
1532
1533     // Since POSIX remove() is specified to work with either files or directories, in a
1534     // perfect world it could just be called. But some important real-world operating
1535     // systems (Windows, Mac OS X, for example) don't implement the POSIX spec. So
1536     // remove_file_or_directory() is always called to kep it simple.
1537     return remove_file_or_directory(p, type, ec);
1538   }
1539
1540   BOOST_FILESYSTEM_DECL
1541   boost::uintmax_t remove_all(const path& p, error_code* ec)
1542   {
1543     error_code tmp_ec;
1544     file_type type = query_file_type(p, &tmp_ec);
1545     if (error(type == status_error, tmp_ec, p, ec,
1546       "boost::filesystem::remove_all"))
1547       return 0;
1548
1549     return (type != status_error && type != file_not_found) // exists
1550       ? remove_all_aux(p, type, ec)
1551       : 0;
1552   }
1553
1554   BOOST_FILESYSTEM_DECL
1555   void rename(const path& old_p, const path& new_p, error_code* ec)
1556   {
1557     error(!BOOST_MOVE_FILE(old_p.c_str(), new_p.c_str()), old_p, new_p, ec,
1558       "boost::filesystem::rename");
1559   }
1560
1561   BOOST_FILESYSTEM_DECL
1562   void resize_file(const path& p, uintmax_t size, system::error_code* ec)
1563   {
1564     error(!BOOST_RESIZE_FILE(p.c_str(), size), p, ec, "boost::filesystem::resize_file");
1565   }
1566
1567   BOOST_FILESYSTEM_DECL
1568   space_info space(const path& p, error_code* ec)
1569   {
1570 #   ifdef BOOST_POSIX_API
1571     struct BOOST_STATVFS vfs;
1572     space_info info;
1573     if (!error(::BOOST_STATVFS(p.c_str(), &vfs)!= 0,
1574       p, ec, "boost::filesystem::space"))
1575     {
1576       info.capacity 
1577         = static_cast<boost::uintmax_t>(vfs.f_blocks)* BOOST_STATVFS_F_FRSIZE;
1578       info.free 
1579         = static_cast<boost::uintmax_t>(vfs.f_bfree)* BOOST_STATVFS_F_FRSIZE;
1580       info.available
1581         = static_cast<boost::uintmax_t>(vfs.f_bavail)* BOOST_STATVFS_F_FRSIZE;
1582     }
1583
1584 #   else
1585     ULARGE_INTEGER avail, total, free;
1586     space_info info;
1587
1588     if (!error(::GetDiskFreeSpaceExW(p.c_str(), &avail, &total, &free)== 0,
1589        p, ec, "boost::filesystem::space"))
1590     {
1591       info.capacity
1592         = (static_cast<boost::uintmax_t>(total.HighPart)<< 32)
1593           + total.LowPart;
1594       info.free
1595         = (static_cast<boost::uintmax_t>(free.HighPart)<< 32)
1596           + free.LowPart;
1597       info.available
1598         = (static_cast<boost::uintmax_t>(avail.HighPart)<< 32)
1599           + avail.LowPart;
1600     }
1601
1602 #   endif
1603
1604     else
1605     {
1606       info.capacity = info.free = info.available = 0;
1607     }
1608     return info;
1609   }
1610
1611   BOOST_FILESYSTEM_DECL
1612   file_status status(const path& p, error_code* ec)
1613   {
1614 #   ifdef BOOST_POSIX_API
1615
1616     struct stat path_stat;
1617     if (::stat(p.c_str(), &path_stat)!= 0)
1618     {
1619       if (ec != 0)                            // always report errno, even though some
1620         ec->assign(errno, system_category());   // errno values are not status_errors
1621
1622       if (not_found_error(errno))
1623       {
1624         return fs::file_status(fs::file_not_found, fs::no_perms);
1625       }
1626       if (ec == 0)
1627         BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::status",
1628           p, error_code(errno, system_category())));
1629       return fs::file_status(fs::status_error);
1630     }
1631     if (ec != 0) ec->clear();;
1632     if (S_ISDIR(path_stat.st_mode))
1633       return fs::file_status(fs::directory_file,
1634         static_cast<perms>(path_stat.st_mode) & fs::perms_mask);
1635     if (S_ISREG(path_stat.st_mode))
1636       return fs::file_status(fs::regular_file,
1637         static_cast<perms>(path_stat.st_mode) & fs::perms_mask);
1638     if (S_ISBLK(path_stat.st_mode))
1639       return fs::file_status(fs::block_file,
1640         static_cast<perms>(path_stat.st_mode) & fs::perms_mask);
1641     if (S_ISCHR(path_stat.st_mode))
1642       return fs::file_status(fs::character_file,
1643         static_cast<perms>(path_stat.st_mode) & fs::perms_mask);
1644     if (S_ISFIFO(path_stat.st_mode))
1645       return fs::file_status(fs::fifo_file,
1646         static_cast<perms>(path_stat.st_mode) & fs::perms_mask);
1647     if (S_ISSOCK(path_stat.st_mode))
1648       return fs::file_status(fs::socket_file,
1649         static_cast<perms>(path_stat.st_mode) & fs::perms_mask);
1650     return fs::file_status(fs::type_unknown);
1651
1652 #   else  // Windows
1653
1654     DWORD attr(::GetFileAttributesW(p.c_str()));
1655     if (attr == 0xFFFFFFFF)
1656     {
1657       return process_status_failure(p, ec);
1658     }
1659
1660     //  reparse point handling;
1661     //    since GetFileAttributesW does not resolve symlinks, try to open a file
1662     //    handle to discover if the file exists
1663     if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
1664     {
1665       handle_wrapper h(
1666         create_file_handle(
1667             p.c_str(),
1668             0,  // dwDesiredAccess; attributes only
1669             FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
1670             0,  // lpSecurityAttributes
1671             OPEN_EXISTING,
1672             FILE_FLAG_BACKUP_SEMANTICS,
1673             0)); // hTemplateFile
1674       if (h.handle == INVALID_HANDLE_VALUE)
1675       {
1676         return process_status_failure(p, ec);
1677       }
1678
1679       if (!is_reparse_point_a_symlink(p))
1680         return file_status(reparse_file, make_permissions(p, attr));
1681     }
1682
1683     if (ec != 0) ec->clear();
1684     return (attr & FILE_ATTRIBUTE_DIRECTORY)
1685       ? file_status(directory_file, make_permissions(p, attr))
1686       : file_status(regular_file, make_permissions(p, attr));
1687
1688 #   endif
1689   }
1690
1691   BOOST_FILESYSTEM_DECL
1692   file_status symlink_status(const path& p, error_code* ec)
1693   {
1694 #   ifdef BOOST_POSIX_API
1695
1696     struct stat path_stat;
1697     if (::lstat(p.c_str(), &path_stat)!= 0)
1698     {
1699       if (ec != 0)                            // always report errno, even though some
1700         ec->assign(errno, system_category());   // errno values are not status_errors
1701
1702       if (errno == ENOENT || errno == ENOTDIR) // these are not errors
1703       {
1704         return fs::file_status(fs::file_not_found, fs::no_perms);
1705       }
1706       if (ec == 0)
1707         BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::status",
1708           p, error_code(errno, system_category())));
1709       return fs::file_status(fs::status_error);
1710     }
1711     if (ec != 0) ec->clear();
1712     if (S_ISREG(path_stat.st_mode))
1713       return fs::file_status(fs::regular_file,
1714         static_cast<perms>(path_stat.st_mode) & fs::perms_mask);
1715     if (S_ISDIR(path_stat.st_mode))
1716       return fs::file_status(fs::directory_file,
1717         static_cast<perms>(path_stat.st_mode) & fs::perms_mask);
1718     if (S_ISLNK(path_stat.st_mode))
1719       return fs::file_status(fs::symlink_file,
1720         static_cast<perms>(path_stat.st_mode) & fs::perms_mask);
1721     if (S_ISBLK(path_stat.st_mode))
1722       return fs::file_status(fs::block_file,
1723         static_cast<perms>(path_stat.st_mode) & fs::perms_mask);
1724     if (S_ISCHR(path_stat.st_mode))
1725       return fs::file_status(fs::character_file,
1726         static_cast<perms>(path_stat.st_mode) & fs::perms_mask);
1727     if (S_ISFIFO(path_stat.st_mode))
1728       return fs::file_status(fs::fifo_file,
1729         static_cast<perms>(path_stat.st_mode) & fs::perms_mask);
1730     if (S_ISSOCK(path_stat.st_mode))
1731       return fs::file_status(fs::socket_file,
1732         static_cast<perms>(path_stat.st_mode) & fs::perms_mask);
1733     return fs::file_status(fs::type_unknown);
1734
1735 #   else  // Windows
1736
1737     DWORD attr(::GetFileAttributesW(p.c_str()));
1738     if (attr == 0xFFFFFFFF)
1739     {
1740       return process_status_failure(p, ec);
1741     }
1742
1743     if (ec != 0) ec->clear();
1744
1745     if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
1746       return is_reparse_point_a_symlink(p)
1747              ? file_status(symlink_file, make_permissions(p, attr))
1748              : file_status(reparse_file, make_permissions(p, attr));
1749
1750     return (attr & FILE_ATTRIBUTE_DIRECTORY)
1751       ? file_status(directory_file, make_permissions(p, attr))
1752       : file_status(regular_file, make_permissions(p, attr));
1753
1754 #   endif
1755   }
1756
1757    // contributed by Jeff Flinn
1758   BOOST_FILESYSTEM_DECL
1759   path temp_directory_path(system::error_code* ec)
1760   {
1761 #   ifdef BOOST_POSIX_API
1762       const char* val = 0;
1763       
1764       (val = std::getenv("TMPDIR" )) ||
1765       (val = std::getenv("TMP"    )) ||
1766       (val = std::getenv("TEMP"   )) ||
1767       (val = std::getenv("TEMPDIR"));
1768       
1769       path p((val!=0) ? val : "/tmp");
1770       
1771       if (p.empty() || (ec&&!is_directory(p, *ec))||(!ec&&!is_directory(p)))
1772       {
1773         errno = ENOTDIR;
1774         error(true, p, ec, "boost::filesystem::temp_directory_path");
1775         return p;
1776       }
1777         
1778       return p;
1779       
1780 #   else  // Windows
1781
1782       std::vector<path::value_type> buf(GetTempPathW(0, NULL));
1783
1784       if (buf.empty() || GetTempPathW(buf.size(), &buf[0])==0)
1785       {
1786         if(!buf.empty()) ::SetLastError(ENOTDIR);
1787         error(true, ec, "boost::filesystem::temp_directory_path");
1788         return path();
1789       }
1790           
1791       buf.pop_back();
1792       
1793       path p(buf.begin(), buf.end());
1794           
1795       if ((ec&&!is_directory(p, *ec))||(!ec&&!is_directory(p)))
1796       {
1797         ::SetLastError(ENOTDIR);
1798         error(true, p, ec, "boost::filesystem::temp_directory_path");
1799         return path();
1800       }
1801       
1802       return p;
1803 #   endif
1804   }
1805   
1806   BOOST_FILESYSTEM_DECL
1807   path system_complete(const path& p, system::error_code* ec)
1808   {
1809 #   ifdef BOOST_POSIX_API
1810     return (p.empty() || p.is_absolute())
1811       ? p : current_path()/ p;
1812
1813 #   else
1814     if (p.empty())
1815     {
1816       if (ec != 0) ec->clear();
1817       return p;
1818     }
1819     wchar_t buf[buf_size];
1820     wchar_t* pfn;
1821     std::size_t len = get_full_path_name(p, buf_size, buf, &pfn);
1822
1823     if (error(len == 0, p, ec, "boost::filesystem::system_complete"))
1824       return path();
1825
1826     if (len < buf_size)// len does not include null termination character
1827       return path(&buf[0]);
1828
1829     boost::scoped_array<wchar_t> big_buf(new wchar_t[len]);
1830
1831     return error(get_full_path_name(p, len , big_buf.get(), &pfn)== 0,
1832       p, ec, "boost::filesystem::system_complete")
1833       ? path()
1834       : path(big_buf.get());
1835 #   endif
1836   }
1837
1838 }  // namespace detail
1839
1840 //--------------------------------------------------------------------------------------//
1841 //                                                                                      //
1842 //                                 directory_entry                                      //
1843 //                                                                                      //
1844 //--------------------------------------------------------------------------------------//
1845
1846   file_status
1847   directory_entry::m_get_status(system::error_code* ec) const
1848   {
1849     if (!status_known(m_status))
1850     {
1851       // optimization: if the symlink status is known, and it isn't a symlink,
1852       // then status and symlink_status are identical so just copy the
1853       // symlink status to the regular status.
1854       if (status_known(m_symlink_status)
1855         && !is_symlink(m_symlink_status))
1856       { 
1857         m_status = m_symlink_status;
1858         if (ec != 0) ec->clear();
1859       }
1860       else m_status = detail::status(m_path, ec);
1861     }
1862     else if (ec != 0) ec->clear();
1863     return m_status;
1864   }
1865
1866   file_status
1867   directory_entry::m_get_symlink_status(system::error_code* ec) const
1868   {
1869     if (!status_known(m_symlink_status))
1870       m_symlink_status = detail::symlink_status(m_path, ec);
1871     else if (ec != 0) ec->clear();
1872     return m_symlink_status;
1873   }
1874
1875 //  dispatch directory_entry supplied here rather than in 
1876 //  <boost/filesystem/path_traits.hpp>, thus avoiding header circularity.
1877 //  test cases are in operations_unit_test.cpp
1878
1879 namespace path_traits
1880 {
1881   void dispatch(const directory_entry & de,
1882 #                ifdef BOOST_WINDOWS_API
1883     std::wstring& to,
1884 #                else   
1885     std::string& to,
1886 #                endif
1887     const codecvt_type &)
1888   {
1889     to = de.path().native();
1890   }
1891
1892   void dispatch(const directory_entry & de,
1893 #                ifdef BOOST_WINDOWS_API
1894     std::wstring& to
1895 #                else   
1896     std::string& to
1897 #                endif
1898     )
1899   {
1900     to = de.path().native();
1901   }
1902 }  // namespace path_traits
1903 } // namespace filesystem
1904 } // namespace boost
1905
1906 //--------------------------------------------------------------------------------------//
1907 //                                                                                      //
1908 //                               directory_iterator                                     //
1909 //                                                                                      //
1910 //--------------------------------------------------------------------------------------//
1911
1912 namespace
1913 {
1914 # ifdef BOOST_POSIX_API
1915
1916   error_code path_max(std::size_t & result)
1917   // this code is based on Stevens and Rago, Advanced Programming in the
1918   // UNIX envirnment, 2nd Ed., ISBN 0-201-43307-9, page 49
1919   {
1920 #   ifdef PATH_MAX
1921     static std::size_t max = PATH_MAX;
1922 #   else
1923     static std::size_t max = 0;
1924 #   endif
1925     if (max == 0)
1926     {
1927       errno = 0;
1928       long tmp = ::pathconf("/", _PC_NAME_MAX);
1929       if (tmp < 0)
1930       {
1931         if (errno == 0)// indeterminate
1932           max = 4096; // guess
1933         else return error_code(errno, system_category());
1934       }
1935       else max = static_cast<std::size_t>(tmp + 1); // relative root
1936     }
1937     result = max;
1938     return ok;
1939   }
1940
1941 #if defined(__PGI) && defined(__USE_FILE_OFFSET64)
1942 #define dirent dirent64
1943 #endif
1944
1945   error_code dir_itr_first(void *& handle, void *& buffer,
1946     const char* dir, string& target,
1947     fs::file_status &, fs::file_status &)
1948   {
1949     if ((handle = ::opendir(dir))== 0)
1950       return error_code(errno, system_category());
1951     target = string(".");  // string was static but caused trouble
1952                              // when iteration called from dtor, after
1953                              // static had already been destroyed
1954     std::size_t path_size (0);  // initialization quiets gcc warning (ticket #3509)
1955     error_code ec = path_max(path_size);
1956     if (ec)return ec;
1957     dirent de;
1958     buffer = std::malloc((sizeof(dirent) - sizeof(de.d_name))
1959       +  path_size + 1); // + 1 for "/0"
1960     return ok;
1961   }  
1962
1963   // warning: the only dirent member updated is d_name
1964   inline int readdir_r_simulator(DIR * dirp, struct dirent * entry,
1965     struct dirent ** result)// *result set to 0 on end of directory
1966   {
1967     errno = 0;
1968
1969 #   if !defined(__CYGWIN__)\
1970     && defined(_POSIX_THREAD_SAFE_FUNCTIONS)\
1971     && defined(_SC_THREAD_SAFE_FUNCTIONS)\
1972     && (_POSIX_THREAD_SAFE_FUNCTIONS+0 >= 0)\
1973     && (!defined(__hpux) || defined(_REENTRANT)) \
1974     && (!defined(_AIX) || defined(__THREAD_SAFE))
1975     if (::sysconf(_SC_THREAD_SAFE_FUNCTIONS)>= 0)
1976       { return ::readdir_r(dirp, entry, result); }
1977 #   endif
1978
1979     struct dirent * p;
1980     *result = 0;
1981     if ((p = ::readdir(dirp))== 0)
1982       return errno;
1983     std::strcpy(entry->d_name, p->d_name);
1984     *result = entry;
1985     return 0;
1986   }
1987
1988   error_code dir_itr_increment(void *& handle, void *& buffer,
1989     string& target, fs::file_status & sf, fs::file_status & symlink_sf)
1990   {
1991     BOOST_ASSERT(buffer != 0);
1992     dirent * entry(static_cast<dirent *>(buffer));
1993     dirent * result;
1994     int return_code;
1995     if ((return_code = readdir_r_simulator(static_cast<DIR*>(handle), entry, &result))!= 0)
1996       return error_code(errno, system_category());
1997     if (result == 0)
1998       return fs::detail::dir_itr_close(handle, buffer);
1999     target = entry->d_name;
2000 #   ifdef BOOST_FILESYSTEM_STATUS_CACHE
2001     if (entry->d_type == DT_UNKNOWN) // filesystem does not supply d_type value
2002     {
2003       sf = symlink_sf = fs::file_status(fs::status_error);
2004     }
2005     else  // filesystem supplies d_type value
2006     {
2007       if (entry->d_type == DT_DIR)
2008         sf = symlink_sf = fs::file_status(fs::directory_file);
2009       else if (entry->d_type == DT_REG)
2010         sf = symlink_sf = fs::file_status(fs::regular_file);
2011       else if (entry->d_type == DT_LNK)
2012       {
2013         sf = fs::file_status(fs::status_error);
2014         symlink_sf = fs::file_status(fs::symlink_file);
2015       }
2016       else sf = symlink_sf = fs::file_status(fs::status_error);
2017     }
2018 #   else
2019     sf = symlink_sf = fs::file_status(fs::status_error);
2020 #    endif
2021     return ok;
2022   }
2023
2024 # else // BOOST_WINDOWS_API
2025
2026   error_code dir_itr_first(void *& handle, const fs::path& dir,
2027     wstring& target, fs::file_status & sf, fs::file_status & symlink_sf)
2028   // Note: an empty root directory has no "." or ".." entries, so this
2029   // causes a ERROR_FILE_NOT_FOUND error which we do not considered an
2030   // error. It is treated as eof instead.
2031   {
2032     // use a form of search Sebastian Martel reports will work with Win98
2033     wstring dirpath(dir.wstring());
2034     dirpath += (dirpath.empty()
2035       || (dirpath[dirpath.size()-1] != L'\\'
2036         && dirpath[dirpath.size()-1] != L'/'
2037         && dirpath[dirpath.size()-1] != L':'))? L"\\*" : L"*";
2038
2039     WIN32_FIND_DATAW data;
2040     if ((handle = ::FindFirstFileW(dirpath.c_str(), &data))
2041       == INVALID_HANDLE_VALUE)
2042     { 
2043       handle = 0;  // signal eof
2044       return error_code( (::GetLastError() == ERROR_FILE_NOT_FOUND
2045                        // Windows Mobile returns ERROR_NO_MORE_FILES; see ticket #3551                                           
2046                        || ::GetLastError() == ERROR_NO_MORE_FILES) 
2047         ? 0 : ::GetLastError(), system_category() );
2048     }
2049     target = data.cFileName;
2050     if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
2051     // reparse points are complex, so don't try to handle them here; instead just mark
2052     // them as status_error which causes directory_entry caching to call status()
2053     // and symlink_status() which do handle reparse points fully
2054     {
2055       sf.type(fs::status_error);
2056       symlink_sf.type(fs::status_error);
2057     }
2058     else
2059     {
2060       if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
2061       {
2062         sf.type(fs::directory_file);
2063         symlink_sf.type(fs::directory_file);
2064       }
2065       else
2066       {
2067         sf.type(fs::regular_file);
2068         symlink_sf.type(fs::regular_file);
2069       }
2070       sf.permissions(make_permissions(data.cFileName, data.dwFileAttributes));
2071       symlink_sf.permissions(sf.permissions());
2072     }
2073     return error_code();
2074   }
2075
2076   error_code  dir_itr_increment(void *& handle, wstring& target,
2077     fs::file_status & sf, fs::file_status & symlink_sf)
2078   {
2079     WIN32_FIND_DATAW data;
2080     if (::FindNextFileW(handle, &data)== 0)// fails
2081     {
2082       int error = ::GetLastError();
2083       fs::detail::dir_itr_close(handle);
2084       return error_code(error == ERROR_NO_MORE_FILES ? 0 : error, system_category());
2085     }
2086     target = data.cFileName;
2087     if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
2088     // reparse points are complex, so don't try to handle them here; instead just mark
2089     // them as status_error which causes directory_entry caching to call status()
2090     // and symlink_status() which do handle reparse points fully
2091     {
2092       sf.type(fs::status_error);
2093       symlink_sf.type(fs::status_error);
2094     }
2095     else
2096     {
2097       if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
2098       {
2099         sf.type(fs::directory_file);
2100         symlink_sf.type(fs::directory_file);
2101       }
2102       else
2103       {
2104         sf.type(fs::regular_file);
2105         symlink_sf.type(fs::regular_file);
2106       }
2107       sf.permissions(make_permissions(data.cFileName, data.dwFileAttributes));
2108       symlink_sf.permissions(sf.permissions());
2109     }
2110     return error_code();
2111   }
2112 #endif
2113
2114   const error_code not_found_error_code (
2115 #     ifdef BOOST_WINDOWS_API
2116         ERROR_PATH_NOT_FOUND
2117 #     else
2118         ENOENT 
2119 #     endif
2120         , system_category());
2121
2122 }  // unnamed namespace
2123
2124 namespace boost
2125 {
2126 namespace filesystem
2127 {
2128
2129 namespace detail
2130 {
2131   //  dir_itr_close is called both from the ~dir_itr_imp()destructor 
2132   //  and dir_itr_increment()
2133   BOOST_FILESYSTEM_DECL
2134   system::error_code dir_itr_close( // never throws
2135     void *& handle
2136 #   if defined(BOOST_POSIX_API)
2137     , void *& buffer
2138 #   endif
2139    )
2140   {
2141 #   ifdef BOOST_POSIX_API
2142     std::free(buffer);
2143     buffer = 0;
2144     if (handle == 0)return ok;
2145     DIR * h(static_cast<DIR*>(handle));
2146     handle = 0;
2147     return error_code(::closedir(h)== 0 ? 0 : errno, system_category());
2148
2149 #   else
2150     if (handle != 0)
2151     {
2152       ::FindClose(handle);
2153       handle = 0;
2154     }
2155     return ok;
2156
2157 #   endif
2158   }
2159
2160   void directory_iterator_construct(directory_iterator& it,
2161     const path& p, system::error_code* ec)    
2162   {
2163     if (error(p.empty(), not_found_error_code, p, ec,
2164               "boost::filesystem::directory_iterator::construct"))
2165       return;
2166
2167     path::string_type filename;
2168     file_status file_stat, symlink_file_stat;
2169     error_code result = dir_itr_first(it.m_imp->handle,
2170 #     if defined(BOOST_POSIX_API)
2171       it.m_imp->buffer,
2172 #     endif
2173       p.c_str(), filename, file_stat, symlink_file_stat);
2174
2175     if (result)
2176     {
2177       it.m_imp.reset();
2178       error(true, result, p,
2179         ec, "boost::filesystem::directory_iterator::construct");
2180       return;
2181     }
2182     
2183     if (it.m_imp->handle == 0)
2184       it.m_imp.reset(); // eof, so make end iterator
2185     else // not eof
2186     {
2187       it.m_imp->dir_entry.assign(p / filename, file_stat, symlink_file_stat);
2188       if (filename[0] == dot // dot or dot-dot
2189         && (filename.size()== 1
2190           || (filename[1] == dot
2191             && filename.size()== 2)))
2192         {  it.increment(*ec); }
2193     }
2194   }
2195
2196   void directory_iterator_increment(directory_iterator& it,
2197     system::error_code* ec)
2198   {
2199     BOOST_ASSERT_MSG(it.m_imp.get(), "attempt to increment end iterator");
2200     BOOST_ASSERT_MSG(it.m_imp->handle != 0, "internal program error");
2201     
2202     path::string_type filename;
2203     file_status file_stat, symlink_file_stat;
2204     system::error_code temp_ec;
2205
2206     for (;;)
2207     {
2208       temp_ec = dir_itr_increment(it.m_imp->handle,
2209 #       if defined(BOOST_POSIX_API)
2210         it.m_imp->buffer,
2211 #       endif
2212         filename, file_stat, symlink_file_stat);
2213
2214       if (temp_ec)  // happens if filesystem is corrupt, such as on a damaged optical disc
2215       {
2216         path error_path(it.m_imp->dir_entry.path().parent_path());  // fix ticket #5900
2217         it.m_imp.reset();
2218         if (ec == 0)
2219           BOOST_FILESYSTEM_THROW(
2220             filesystem_error("boost::filesystem::directory_iterator::operator++",
2221               error_path,
2222               error_code(BOOST_ERRNO, system_category())));
2223         ec->assign(BOOST_ERRNO, system_category());
2224         return;
2225       }
2226       else if (ec != 0) ec->clear();
2227
2228       if (it.m_imp->handle == 0)  // eof, make end
2229       {
2230         it.m_imp.reset();
2231         return;
2232       }
2233
2234       if (!(filename[0] == dot // !(dot or dot-dot)
2235         && (filename.size()== 1
2236           || (filename[1] == dot
2237             && filename.size()== 2))))
2238       {
2239         it.m_imp->dir_entry.replace_filename(
2240           filename, file_stat, symlink_file_stat);
2241         return;
2242       }
2243     }
2244   }
2245 }  // namespace detail
2246 } // namespace filesystem
2247 } // namespace boost