E-MailRelay
gdirectory_win32.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2023 Graeme Walker <graeme_walker@users.sourceforge.net>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16// ===
17///
18/// \file gdirectory_win32.cpp
19///
20
21#include "gdef.h"
22#include "gdirectory.h"
23#include "gfile.h"
24#include "glog.h"
25#include "gassert.h"
26#include <iomanip>
27#include <cerrno>
28#include <fcntl.h>
29#include <io.h>
30#include <share.h>
31
32#ifndef INVALID_FILE_ATTRIBUTES
33#define INVALID_FILE_ATTRIBUTES 0xFFFFFFFF
34#endif
35
36namespace G
37{
38 class DirectoryIteratorImp ;
39}
40
41int G::Directory::usable( bool /*for_creation*/ ) const
42{
43 DWORD attributes = ::GetFileAttributesA( m_path.cstr() ) ;
44 if( attributes == INVALID_FILE_ATTRIBUTES )
45 {
46 DWORD e = ::GetLastError() ;
47 if( e == ERROR_ACCESS_DENIED || e == ERROR_NETWORK_ACCESS_DENIED )
48 return EACCES ;
49 return ENOENT ;
50 }
51 return ( attributes & FILE_ATTRIBUTE_DIRECTORY ) ? 0 : ENOTDIR ;
52}
53
54bool G::Directory::writeable( const std::string & filename ) const
55{
56 Path path( m_path , filename.empty() ? tmp() : filename ) ;
57 return File::probe( path.cstr() ) ;
58}
59
60// ===
61
62//| \class G::DirectoryIteratorImp
63/// A pimple-pattern implementation class for DirectoryIterator.
64///
65class G::DirectoryIteratorImp
66{
67public:
68 explicit DirectoryIteratorImp( const Directory &dir ) ;
69 ~DirectoryIteratorImp() ;
70 bool isDir() const ;
71 bool more() ;
72 bool error() const ;
73 std::string sizeString() const ;
74 Path filePath() const ;
75 std::string fileName() const ;
76
77public:
78 DirectoryIteratorImp( const DirectoryIteratorImp & ) = delete ;
79 DirectoryIteratorImp( DirectoryIteratorImp && ) = delete ;
80 DirectoryIteratorImp & operator=( const DirectoryIteratorImp & ) = delete ;
81 DirectoryIteratorImp & operator=( DirectoryIteratorImp && ) = delete ;
82
83private:
84 WIN32_FIND_DATAA m_context ;
85 HANDLE m_handle ;
86 Directory m_dir ;
87 bool m_error ;
88 bool m_first ;
89} ;
90
91// ===
92
93G::DirectoryIterator::DirectoryIterator( const Directory & dir ) :
94 m_imp(std::make_unique<DirectoryIteratorImp>(dir))
95{
96}
97
99{
100 return m_imp->error() ;
101}
102
104{
105 return m_imp->more() ;
106}
107
109{
110 return m_imp->filePath() ;
111}
112
113std::string G::DirectoryIterator::fileName() const
114{
115 return m_imp->fileName() ;
116}
117
119{
120 return m_imp->isDir() ;
121}
122
124{
125 return false ;
126}
127
128std::string G::DirectoryIterator::sizeString() const
129{
130 return m_imp->sizeString() ;
131}
132
134= default ;
135
136// ===
137
138G::DirectoryIteratorImp::DirectoryIteratorImp( const Directory & dir ) :
139 m_dir(dir) ,
140 m_error(false) ,
141 m_first(true)
142{
143 m_handle = FindFirstFileA( (dir.path()+"*").cstr() , &m_context ) ;
144 if( m_handle == INVALID_HANDLE_VALUE )
145 {
146 DWORD err = ::GetLastError() ;
147 if( err != ERROR_FILE_NOT_FOUND )
148 m_error = true ;
149 }
150}
151
152bool G::DirectoryIteratorImp::error() const
153{
154 return m_error ;
155}
156
157bool G::DirectoryIteratorImp::more()
158{
159 if( m_handle == INVALID_HANDLE_VALUE )
160 return false ;
161
162 if( m_first )
163 {
164 m_first = false ;
165 if( std::string(m_context.cFileName) != "." && std::string(m_context.cFileName) != ".." )
166 return true ;
167 }
168
169 for(;;)
170 {
171 bool rc = FindNextFileA( m_handle , &m_context ) != 0 ;
172 if( !rc )
173 {
174 DWORD err = ::GetLastError() ;
175 if( err != ERROR_NO_MORE_FILES )
176 m_error = true ;
177
178 FindClose( m_handle ) ;
179 m_handle = INVALID_HANDLE_VALUE ;
180 return false ;
181 }
182
183 // go round again if . or ..
184 if( std::string(m_context.cFileName) != "." && std::string(m_context.cFileName) != ".." )
185 break ;
186 }
187
188 return true ;
189}
190
191G::Path G::DirectoryIteratorImp::filePath() const
192{
193 G_ASSERT( m_handle != INVALID_HANDLE_VALUE ) ;
194 return m_dir.path() + m_context.cFileName ;
195}
196
197std::string G::DirectoryIteratorImp::fileName() const
198{
199 G_ASSERT( m_handle != INVALID_HANDLE_VALUE ) ;
200 return m_context.cFileName ;
201}
202
203bool G::DirectoryIteratorImp::isDir() const
204{
205 return !! ( m_context.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ;
206}
207
208G::DirectoryIteratorImp::~DirectoryIteratorImp()
209{
210 if( m_handle != INVALID_HANDLE_VALUE )
211 FindClose( m_handle ) ;
212}
213
214std::string G::DirectoryIteratorImp::sizeString() const
215{
216 const DWORD & hi = m_context.nFileSizeHigh ;
217 const DWORD & lo = m_context.nFileSizeLow ;
218
219 __int64 n = hi ;
220 n <<= 32 ;
221 n |= lo ;
222 if( n == 0 )
223 return "0" ;
224 std::string s ;
225 while( n != 0 )
226 {
227 int i = static_cast<int>( n % 10 ) ;
228 char c = static_cast<char>( '0' + i ) ;
229 s.insert( 0U , 1U , c ) ;
230 n /= 10 ;
231 }
232 return s ;
233}
234
DirectoryIterator(const Directory &dir)
Constructor taking a directory reference.
std::string fileName() const
Returns the name of the current item.
bool error() const
Returns true on error. The caller should stop the iteration.
std::string sizeString() const
Returns the file size as a decimal string.
bool isLink() const
Returns true if the current item is a symlink.
~DirectoryIterator()
Destructor.
bool isDir() const
Returns true if the current item is a directory.
bool more()
Returns true if more and advances by one.
Path filePath() const
Returns the path of the current item.
int usable(bool for_creating_files=false) const
Returns zero if the object represents a valid directory with permissions that dont disallow reading o...
bool writeable(const std::string &probe_filename=tmp()) const
Tries to create and then delete an empty test file in the directory.
static bool probe(const char *) noexcept
Creates and deletes a temporary probe file.
Definition: gfile_unix.cpp:114
A Path object represents a file system path.
Definition: gpath.h:73
const char * cstr() const noexcept
Returns the path string.
Definition: gpath.h:230
Low-level classes.
Definition: garg.h:30
STL namespace.