E-MailRelay
gdirectory_win32.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2024 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 "gnowide.h"
23#include "gdirectory.h"
24#include "gfile.h"
25#include "gconvert.h"
26#include "glog.h"
27#include "gassert.h"
28#include <iomanip>
29#include <cerrno>
30#include <fcntl.h>
31#include <io.h>
32#include <share.h>
33
34#ifndef INVALID_FILE_ATTRIBUTES
35#define INVALID_FILE_ATTRIBUTES 0xFFFFFFFF
36#endif
37
38namespace G
39{
40 class DirectoryIteratorImp ;
41}
42
43class G::DirectoryIteratorImp
44{
45public:
46 explicit DirectoryIteratorImp( const Directory & dir ) ;
47 ~DirectoryIteratorImp() ;
48 bool isDir() const ;
49 bool more() ;
50 bool error() const ;
51 std::string sizeString() const ;
52 Path filePath() const ;
53 std::string fileName() const ;
54
55public:
56 DirectoryIteratorImp( const DirectoryIteratorImp & ) = delete ;
57 DirectoryIteratorImp( DirectoryIteratorImp && ) = delete ;
58 DirectoryIteratorImp & operator=( const DirectoryIteratorImp & ) = delete ;
59 DirectoryIteratorImp & operator=( DirectoryIteratorImp && ) = delete ;
60
61private:
62 static bool oneDot( const wchar_t * p ) noexcept { return p[0] == L'.' && p[1] == 0 ; }
63 static bool twoDots( const wchar_t * p ) noexcept { return p[0] == L'.' && p[1] == L'.' && p[2] == 0 ; }
64 static bool oneDot( const char * p ) noexcept { return p[0] == '.' && p[1] == 0 ; }
65 static bool twoDots( const char * p ) noexcept { return p[0] == '.' && p[1] == '.' && p[2] == 0 ; }
66
67private:
68 nowide::FIND_DATA_type m_context ;
69 HANDLE m_handle ;
70 Directory m_dir ;
71 bool m_error ;
72 bool m_first ;
73} ;
74
75// ==
76
77int G::Directory::usable( bool /*for_creation*/ ) const
78{
79 DWORD attributes = nowide::getFileAttributes( m_path ) ;
80 if( attributes == INVALID_FILE_ATTRIBUTES )
81 {
82 DWORD e = GetLastError() ;
83 if( e == ERROR_ACCESS_DENIED || e == ERROR_NETWORK_ACCESS_DENIED )
84 return EACCES ;
85 return ENOENT ;
86 }
87 return ( attributes & FILE_ATTRIBUTE_DIRECTORY ) ? 0 : ENOTDIR ;
88}
89
90bool G::Directory::writeable( const std::string & filename ) const
91{
92 Path path( m_path , filename.empty() ? tmp() : filename ) ;
93 return File::probe( path ) ;
94}
95
96// ==
97
98G::DirectoryIterator::DirectoryIterator( const Directory & dir ) :
99 m_imp(std::make_unique<DirectoryIteratorImp>(dir))
100{
101}
102
104{
105 return m_imp->error() ;
106}
107
109{
110 return m_imp->more() ;
111}
112
114{
115 return m_imp->filePath() ;
116}
117
118std::string G::DirectoryIterator::fileName() const
119{
120 return m_imp->fileName() ;
121}
122
124{
125 return m_imp->isDir() ;
126}
127
129{
130 return false ;
131}
132
133std::string G::DirectoryIterator::sizeString() const
134{
135 return m_imp->sizeString() ;
136}
137
139= default ;
140
141// ===
142
143G::DirectoryIteratorImp::DirectoryIteratorImp( const Directory & dir ) :
144 m_dir(dir) ,
145 m_error(false) ,
146 m_first(true)
147{
148 m_handle = nowide::findFirstFile( dir.path()/"*" , &m_context ) ;
149 if( m_handle == INVALID_HANDLE_VALUE )
150 {
151 DWORD err = GetLastError() ;
152 if( err != ERROR_FILE_NOT_FOUND )
153 m_error = true ;
154 }
155}
156
157bool G::DirectoryIteratorImp::error() const
158{
159 return m_error ;
160}
161
162bool G::DirectoryIteratorImp::more()
163{
164 if( m_handle == INVALID_HANDLE_VALUE )
165 return false ;
166
167 if( m_first )
168 {
169 m_first = false ;
170 if( !oneDot(m_context.cFileName) && !twoDots(m_context.cFileName) )
171 return true ;
172 }
173
174 for(;;)
175 {
176 bool rc = nowide::findNextFile( m_handle , &m_context ) != 0 ;
177 if( !rc )
178 {
179 DWORD err = GetLastError() ;
180 if( err != ERROR_NO_MORE_FILES )
181 m_error = true ;
182
183 FindClose( m_handle ) ;
184 m_handle = INVALID_HANDLE_VALUE ;
185 return false ;
186 }
187
188 // go round again if . or ..
189 if( !oneDot(m_context.cFileName) && !twoDots(m_context.cFileName) )
190 break ;
191 }
192
193 return true ;
194}
195
196G::Path G::DirectoryIteratorImp::filePath() const
197{
198 G_ASSERT( m_handle != INVALID_HANDLE_VALUE ) ;
199 return m_dir.path() / fileName() ;
200}
201
202std::string G::DirectoryIteratorImp::fileName() const
203{
204 G_ASSERT( m_handle != INVALID_HANDLE_VALUE ) ;
205 return nowide::cFileName( m_context ) ;
206}
207
208bool G::DirectoryIteratorImp::isDir() const
209{
210 return !! ( m_context.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ;
211}
212
213G::DirectoryIteratorImp::~DirectoryIteratorImp()
214{
215 if( m_handle != INVALID_HANDLE_VALUE )
216 FindClose( m_handle ) ;
217}
218
219std::string G::DirectoryIteratorImp::sizeString() const
220{
221 const DWORD & hi = m_context.nFileSizeHigh ;
222 const DWORD & lo = m_context.nFileSizeLow ;
223
224 __int64 n = hi ;
225 n <<= 32 ;
226 n |= lo ;
227 if( n == 0 )
228 return "0" ;
229 std::string s ;
230 while( n != 0 )
231 {
232 int i = static_cast<int>( n % 10 ) ;
233 char c = static_cast<char>( '0' + i ) ;
234 s.insert( 0U , 1U , c ) ;
235 n /= 10 ;
236 }
237 return s ;
238}
239
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 or a symlink to 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 Path &) noexcept
Creates and deletes a temporary probe file.
Definition: gfile_unix.cpp:120
A Path object represents a file system path.
Definition: gpath.h:82
Contains inline functions that convert to and from UTF-8 strings in order to call wide-character "W()...
Low-level classes.
Definition: garg.h:36
STL namespace.