E-MailRelay
gdirectory_unix.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_unix.cpp
19///
20
21#include "gdef.h"
22#include "gdirectory.h"
23#include "gprocess.h"
24#include "gdatetime.h"
25#include "gfile.h"
26#include "glog.h"
27#include <sstream>
28#include <sys/stat.h>
29#include <sys/types.h>
30#include <dirent.h>
31#include <unistd.h>
32#include <fcntl.h>
33
34namespace G
35{
36 class DirectoryIteratorImp ;
37}
38
39//| \class G::DirectoryIteratorImp
40/// A pimple-pattern implementation class for DirectoryIterator using
41/// opendir()/readdir().
42///
43class G::DirectoryIteratorImp
44{
45public:
46 explicit DirectoryIteratorImp( const Directory & dir ) ;
47 ~DirectoryIteratorImp() ;
48 bool isDir() const ;
49 bool isLink() const ;
50 bool more() ;
51 bool error() const ;
52 std::string sizeString() const ;
53 Path filePath() const ;
54 std::string fileName() const ;
55
56public:
57 DirectoryIteratorImp( const DirectoryIteratorImp & ) = delete ;
58 DirectoryIteratorImp( DirectoryIteratorImp && ) = delete ;
59 DirectoryIteratorImp & operator=( const DirectoryIteratorImp & ) = delete ;
60 DirectoryIteratorImp & operator=( DirectoryIteratorImp && ) = delete ;
61
62private:
63 DIR * m_d {nullptr} ;
64 struct dirent * m_dp {nullptr} ;
65 Directory m_dir ;
66 bool m_error {true} ;
67 bool m_is_dir {false} ;
68 bool m_is_link {false} ;
69} ;
70
71//
72
73int G::Directory::usable( bool for_creation ) const
74{
75 if( m_path.empty() )
76 return ENOTDIR ;
77
78 // use opendir("foo/.") rather than opendir("foo") to verify
79 // that any contained files can be stat()ed -- ie. that all
80 // directory parts in the m_path have '--x'
81 std::string path_dot = m_path.str() + (m_path.str()=="/"?"":"/") + "." ;
82
83 DIR * p = ::opendir( path_dot.c_str() ) ;
84 int error = p == nullptr ? Process::errno_() : 0 ;
85 if( p != nullptr )
86 ::closedir( p ) ;
87
88 if( error == 0 && for_creation )
89 {
90 // (not definitive -- see also GNU/Linux ::euidaccess())
91 int rc = ::access( m_path.cstr() , W_OK ) ;
92 error = rc == 0 ? 0 : Process::errno_() ;
93 }
94 return error ;
95}
96
97bool G::Directory::writeable( const std::string & filename ) const
98{
99 Path path( m_path , filename.empty() ? tmp() : filename ) ;
100 return File::probe( path ) ;
101}
102
103// ===
104
106 m_imp(std::make_unique<DirectoryIteratorImp>(dir))
107{
108}
109
111= default ;
112
114{
115 return m_imp->error() ;
116}
117
119{
120 return m_imp->more() ;
121}
122
124{
125 return m_imp->filePath() ;
126}
127
129{
130 return m_imp->fileName() ;
131}
132
134{
135 return m_imp->isLink() ;
136}
137
139{
140 return m_imp->isDir() ;
141}
142
143#ifndef G_LIB_SMALL
145{
146 return m_imp->sizeString() ;
147}
148#endif
149
150// ===
151
152G::DirectoryIteratorImp::DirectoryIteratorImp( const Directory & dir ) :
153 m_dir(dir)
154{
155 m_d = ::opendir( dir.path().cstr() ) ;
156 m_error = m_d == nullptr ; // NOLINT
157}
158
159bool G::DirectoryIteratorImp::error() const
160{
161 return m_error ;
162}
163
164bool G::DirectoryIteratorImp::more()
165{
166 while( !m_error )
167 {
168 m_dp = ::readdir( m_d ) ;
169 m_error = m_dp == nullptr ;
170 if( m_error )
171 break ;
172
173 static_assert( sizeof(dirent::d_name) > 2U , "" ) ;
174 bool special = m_dp->d_name[0] == '.' && (
175 ( m_dp->d_name[1] == '\0' ) ||
176 ( m_dp->d_name[1] == '.' && m_dp->d_name[2] == '\0' ) ) ;
177
178 if( special )
179 {
180 m_is_dir = true ;
181 m_is_link = false ;
182 }
183 #if GCONFIG_HAVE_DIRENT_D_TYPE
184 else if( m_dp->d_type != DT_UNKNOWN )
185 {
186 m_is_dir = m_dp->d_type == DT_DIR ;
187 m_is_link = m_dp->d_type == DT_LNK ;
188 if( m_is_link )
189 m_is_dir = File::isDirectory(filePath(),std::nothrow) ;
190 }
191 #endif
192 else
193 {
194 auto statbuf = File::stat( filePath() , /*symlink_nofollow=*/true ) ;
195 m_error = statbuf.error != 0 ;
196 m_is_dir = !m_error && statbuf.is_dir ;
197 m_is_link = !m_error && statbuf.is_link ;
198 if( m_is_link )
199 {
200 statbuf = File::stat( filePath() , /*symlink_nofollow=*/false ) ;
201 m_is_dir = statbuf.error != 0 && statbuf.is_dir ;
202 }
203 }
204 if( !special )
205 break ;
206 }
207 return !m_error ;
208}
209
210G::Path G::DirectoryIteratorImp::filePath() const
211{
212 return m_dir.path() / fileName() ;
213}
214
215std::string G::DirectoryIteratorImp::fileName() const
216{
217 return m_dp == nullptr ? std::string() : std::string(m_dp->d_name) ;
218}
219
220bool G::DirectoryIteratorImp::isDir() const
221{
222 return m_is_dir ;
223}
224
225bool G::DirectoryIteratorImp::isLink() const
226{
227 return m_is_link ;
228}
229
230G::DirectoryIteratorImp::~DirectoryIteratorImp()
231{
232 if( m_d != nullptr )
233 ::closedir( m_d ) ;
234}
235
236std::string G::DirectoryIteratorImp::sizeString() const
237{
238 std::string s = File::sizeString( filePath() ) ;
239 return s.empty() ? std::string(1U,'0') : s ;
240}
241
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.
An encapsulation of a file system directory that works with G::DirectoryIterator.
Definition: gdirectory.h:48
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.
Path path() const
Returns the directory's path, as passed in to the ctor.
Definition: gdirectory.cpp:50
static bool isDirectory(const Path &path, std::nothrow_t)
Returns true if the path exists() and is a directory.
Definition: gfile.cpp:179
static std::string sizeString(const Path &file)
Returns the file's size in string format.
Definition: gfile.cpp:199
static Stat stat(const Path &path, bool symlink_nofollow=false)
Returns a file status structure.
Definition: gfile.cpp:174
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
const value_type * cstr() const noexcept
Returns the path's c-string.
Definition: gpath.h:249
std::string str() const
Returns the path string.
Definition: gpath.h:243
bool empty() const noexcept
Returns true if the path is empty.
Definition: gpath.h:237
static int errno_(const SignalSafe &=G::SignalSafe()) noexcept
Returns the process's current 'errno' value.
Low-level classes.
Definition: garg.h:36
STL namespace.